3
\$\begingroup\$

So this was inspired by: this question But doesn't actually answer it: What do you think of:

public class Case<TRes>
{
    public Case(bool condition, Func<TRes> result)
    {
        Condition = condition;
        Result = result;
    }
    public bool Condition {get; private set;}
    public Func<TRes> Result { get; private set; }

    public static Case<TRes> Default(Func<TRes> value)
    {
        return new Case<TRes>(true, value);
    }
}

public static class Logic
{
    public static T Match<T> (params Case<T>[] cases)
    {
        return cases.First(c => c.Condition).Result();
    }
}

Which you could invoke:

int i = Logic.Match
(
    new Case<int>(IsCloudy(skyImage), () =>
    {
        ChangeWeatherIcon(Icons.Clouds);
        return 13;
    }),
    new Case<int>(a < b, () => a),
    Case<int>.Default(() => {throw new Exception(); })
);

it's purpose isn't quiet to emulate F#'s Match. (Because F# match doens't actually take conditions, it is a stuctural/pattern match) So really its kind of like the trinary operator, meets the nonOrdinal Select statment.

Anyone got any idea's on how to make it work cleaner? Maybe getting a way to get rid of the new's and the brackets

\$\endgroup\$
  • \$\begingroup\$ If we swap Func(T) for T, we get rid of the brackets etc, at the expense of nolonger being able to specify things to be done. (so it would be more like the trinary operator than a If) \$\endgroup\$ – Lyndon White Oct 2 '11 at 14:04
  • \$\begingroup\$ You might be interested in reading this blog post which has been doing something similar. It uses expressions and analyzes them to perform the matching. It's more or less the approach I had in mind. Take a look. \$\endgroup\$ – Jeff Mercado Oct 3 '11 at 18:46
1
\$\begingroup\$

To be honest, it's pretty much impossible to do elegant pattern matching in a language that doesn't support it (e.g., C#). The problem with what you've written there is it's more complicated than the equivalent if-then-else chain:

if (IsCloudy(skyImage)) {
    ChangeWeatherIcon(Icons.Clouds);
    return 13;
}
if (a < b) {
    throw new Exception();
}

Now, I would dearly love to see some later version of C# adopt algebraic data types (a la F#) with pattern matching, but until then I very much doubt you'll be able to do better than if-then-else chains.

\$\endgroup\$
  • \$\begingroup\$ I'm not sure if you realise, but your if/else change is not (at all) equivelent. Equivelent is: int i; if (IsCloudy(skyImage) { ChangeWeatherIcon(Icons.Clouds); i = 13; } else if (a < b) i= a; else throw new Exception(); It would be alot nicer spread over multiple lines \$\endgroup\$ – Lyndon White Oct 3 '11 at 11:15
  • 1
    \$\begingroup\$ You're right, but - meh - that's beside my point, which is that in attempting to do something more elegant than if-then-elses, you've made your code harder to read. \$\endgroup\$ – Rafe Oct 3 '11 at 23:12
1
\$\begingroup\$

I generally agree with Rafe: it's not worth trying to do this in C# for the general case. Using expressions, etc, simply don't cut it, IMHO pattern matching is something that must be built into the language (or use a malleable language like Lisp). The main point of pattern matching is that it's exhaustive (or at least you get a warning if it's not), and therefore total. If you can't guarantee that, then you don't gain anything.

However, I do think it's fruitful to use pattern matching for specific cases. For example, in FSharpx we have pattern matching for C# on options, lists and choices (i.e. anonymous discriminated unions).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.