Question

I'm in the situation where a lot of my classes are containers of well-known but unordered objects of different types, e.g. a container may look as follows:

public class Container
{
    public A A { get; private set; }
    public B B { get; private set; }
    public C C { get; private set; }

    public bool StoreIfKnown(object o)
    {
        // TODO...
    }
}

So if o is of type A it should be stored in the A property, type B in the B property and so on.

In F# the StoreIfKnown method could be written something like the following (excuse the syntax errors, my F# is not great and quite rusty):

match o with
| ?: A a -> A <- a; true
| ?: B b -> B <- b; true
| ?: C c -> C <- c; true
| _ -> false

But in C# the only way seems to be the rather verbose:

if (o is A)
{
    this.A = (A)o;
    return true;
}

if (o is B)
{
    this.B = (B)o;
    return true;
}

// etc.

return false;

I could do it with the as keyword to avoid the test/cast pattern which would be faster, but is even more verbose.

Is there any elegant way to do this in C#?

Was it helpful?

Solution

You could author an extension method on 'o' and helper classes to enable a programming model like

o.Match<A>( a => { this.A = a; return true; } )
 .Match<B>( b => { this.B = b; return true; } )
 .Else( () => { return false; } )

But be wary of doing too much DSL-like hackery here, lest you end up with an API only you understand.

See also

http://blogs.msdn.com/lucabol/archive/2008/07/15/a-c-library-to-write-functional-code-part-v-the-match-operator.aspx

OTHER TIPS

Its not as nifty as Brian's solution, but this doesn't require defining a new DSL. You'll notice your repeating the following code:

if (o is {DataType})
{
    {Property} = ({DataType})o;
    return true;
}

Its easy enough to pull that template into its own method, resulting in something like this:

public class Container
{
    public A A { get; private set; }
    public B B { get; private set; }
    public C C { get; private set; }

    private bool TestProp<T>(object o, Action<T> f)
    {
        if (o is T)
            return false;

        f((T)o);
        return true;
    }

    public bool StoreIfKnown(object o)
    {
        return
            TestProp<A>(o, x => A = x) ||
            TestProp<B>(o, x => B = x) ||
            TestProp<C>(o, x => C = x) ||
            false;
    }
}

If you're working with reference types, you can take advantage of type inference with the following adjustments:

    private bool TestProp<T>(T o, Action<T> f)
    {
        if (o == null)
            return false;

        f(o);
        return true;
    }

    public bool StoreIfKnown(object o)
    {
        return
            TestProp(o as A, x => A = x) ||
            TestProp(o as B, x => B = x) ||
            TestProp(o as C, x => C = x) ||
            false;
    }

I've been playing around with a little match builder (inspired by Brian's answer) which allows type checking, guard clauses, and returning of a result from the whole thing. It uses type inference so the only place you need to specify a type is where you actually want to.

So, imagining type C has an IsActive property which we want to be true, it would look something like this:

var stored = Match.Against(o)
    .When<A>().Then(a => { this.A = a; return true; })
    .When<B>().Then(b => { this.B = b; return true; })
    .When<C>(c => c.IsActive).Then(c => { this.C = c; return true; })
    .Otherwise(a => false);

Which I think is pretty readable, especially as it allows a predicate to be run against the derived type before actually matching which is something I do need.

The code is quite lengthy as it needs a number of partially-specified builder classes in the background to allow the type inference to work, so I can't really post it here. But if anyone's interested let me know in the comments and I'll stick it up on my blog and put a link here.

Bart de Smet once went crazy with pattern matching, starting here (goes all the way up to part 8). If you ever manage to get through all this content there shouldn't be any questions left to pattern matching in C#. If there are, they probably cannot be answered by stackoverflow :)

As of August 2016 and preview of C# 7.0, there is a limited support for pattern matching. You can try if by using Visual Studio “15” Preview 4.

According to the MSDN blog, you can use patterns in two places:

  • on the right-hand side of is expressions

  • in the case clauses in switch statements

Possible patterns are:

  • Constant patterns of the form c (where c is a constant expression in C#), which test that the input is equal to c

  • Type patterns of the form T x (where T is a type and x is an identifier), which test that the input has type T, and if so, extracts the value of the input into a fresh variable x of type T

  • Var patterns of the form var x (where x is an identifier), which always match, and simply put the value of the input into a fresh variable x with the same type as the input

I didn't install Visual Studio 15, so I'm not sure I rewrote your code correctly, but it should not be far off:

public class Container
{
    public A A { get; private set; }
    public B B { get; private set; }
    public C C { get; private set; }

    public bool StoreIfKnown(object obj)
    {
        switch (obj)
        {
            case A a:
                this.A = a
                // I don't put "break" because I'm returning value from a method
                return true;
            case B b:
                this.B = b
                return true;
            case C c:
                this.C = c
                return true;
            default:
                WriteLine("<other>");
                return false;
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top