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#?
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
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 :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With