When it comes to runtime type comparison, there are multiple ways to do so, it's more cumbersome if you want to do it multiple times with if else
. it would be really nice if we could literally switch
on System.Type
or System.RuntimeTypeHandle
and compiler choose the fastest way, like using TypeHandle
.
Type type = ...;
switch(type) {
case typeof(Foo): ... break;
case typeof(Bar): ... break;
}
It's planned that you can switch on type in C# 7.
I think it will look like this:
switch(baz)
{
case Foo foo:
foo.DoFoo();
break;
case Bar bar:
bar.DoBar();
break;
}
@GeirGrusom That's not what I'm talking about.
Well, it would be nice if types were first-class citizens, but that would require CLR support, I think.
@orthoxerox I even suspect that this behavior can be neatly implemented as an extension is
operator (if that would be possible). So, no CLR support is required at all.
The CLR is fully capable of handling the logic. The question is how such a statement should be expanded. I'd imagine it would produce the same IL as a series of if
/else
statements since the IL switch
opcode only works when the values are a range starting at 0 which the runtime type handle would not be.
Type type = obj.GetType();
switch (type)
{
case typeof(Foo):
Console.WriteLine("Foo!");
break;
case typeof(Bar):
Console.WriteLine("Foo!");
break;
case typeof(Baz):
default:
Console.WriteLine("Baz or Something Else!");
break;
case typeof(Frob):
Console.WriteLine("Frob!");
break;
}
effectively equivalent to:
Type type = obj.GetType();
if (typeof(Foo).IsEquivalentTo(type))
{
Console.WriteLine("Foo!");
}
else if (typeof(Bar).IsEquivalentTo(type))
{
Console.WriteLine("Bar!");
}
else if (typeof(Frob).IsEquivalentTo(type))
{
Console.WriteLine("Frob!");
}
else
{
Console.WriteLine("Baz or Something Else!");
}
@HaloFour switch
with pattern-matching already turns to mix of if
, else
and switch
(at least this is what F# does). So I assume this can be done in a similar way. Also, I don't know about IsEquivalentTo
but comparing TypeHandle
causes no type load and will be emitted directly to IL (see the link in the opening post).
@alrz In IL it's all conditional branch opcodes, unless the condition meets the incredibly narrow requirements to make it work with switch
. I assume that ADTs in F# have an incrementing tag field which do meet those requirements.
If if (typeof(Frob).TypeHandle.Equals(type))
is appreciably more efficient then sure, that's probably what the compiler should emit.
Should this only match exact types? What about derived types? Would case typeof(object)
catch everything or only new object()
? Either way it's going to surprise someone somewhere.
@drewnoakes That would be actually nice but then it will depend on the order and we might lose TypeHandle
optimization.
Or it'll turn it into a dictionary lookup, similar to VB.net's Select Case
Seems you can do it in future branch
But the compiler will crash when compiling it :(
@CnSimonChan I think that's because switch
is already relaxed to accept any expression type (depending on the branch you're on) and this doesn't seem to be something that you'd get it "for free" via pattern-matching. So I think they should make it explicitly an error or consider implementing it.
@CnSimonChan Reported as #10459
Since we have to fix the compiler crash, lets just make this work.
A compelling feature I come a lot across with. I'd love to see this implemented.
Issue moved to dotnet/csharplang #356 via ZenHub
Most helpful comment
Since we have to fix the compiler crash, lets just make this work.