C# Switch on Type

C# switch on type

See another answer; this feature now exists in C#


I usually use a dictionary of types and delegates.

var @switch = new Dictionary<Type, Action> {
{ typeof(Type1), () => ... },
{ typeof(Type2), () => ... },
{ typeof(Type3), () => ... },
};

@switch[typeof(MyType)]();

It's a little less flexible as you can't fall through cases, continue etc. But I rarely do so anyway.

Is there a better alternative than this to 'switch on type'?

Switching on types is definitely lacking in C# (UPDATE: in C#7 / VS 2017 switching on types is supported - see Zachary Yates's answer). In order to do this without a large if/else if/else statement, you'll need to work with a different structure. I wrote a blog post awhile back detailing how to build a TypeSwitch structure.

https://learn.microsoft.com/archive/blogs/jaredpar/switching-on-types

Short version: TypeSwitch is designed to prevent redundant casting and give a syntax that is similar to a normal switch/case statement. For example, here is TypeSwitch in action on a standard Windows form event

TypeSwitch.Do(
sender,
TypeSwitch.Case<Button>(() => textBox1.Text = "Hit a Button"),
TypeSwitch.Case<CheckBox>(x => textBox1.Text = "Checkbox is " + x.Checked),
TypeSwitch.Default(() => textBox1.Text = "Not sure what is hovered over"));

The code for TypeSwitch is actually pretty small and can easily be put into your project.

static class TypeSwitch {
public class CaseInfo {
public bool IsDefault { get; set; }
public Type Target { get; set; }
public Action<object> Action { get; set; }
}

public static void Do(object source, params CaseInfo[] cases) {
var type = source.GetType();
foreach (var entry in cases) {
if (entry.IsDefault || entry.Target.IsAssignableFrom(type)) {
entry.Action(source);
break;
}
}
}

public static CaseInfo Case<T>(Action action) {
return new CaseInfo() {
Action = x => action(),
Target = typeof(T)
};
}

public static CaseInfo Case<T>(Action<T> action) {
return new CaseInfo() {
Action = (x) => action((T)x),
Target = typeof(T)
};
}

public static CaseInfo Default(Action action) {
return new CaseInfo() {
Action = x => action(),
IsDefault = true
};
}
}

c# 7.0: switch on System.Type

The (already linked) new pattern matching feature allows this.

Ordinarily, you'd switch on a value:

switch (this.value) {
case int intValue:
this.value = Math.Max(Math.Min(intValue, Maximum), Minimum);
break;
case decimal decimalValue:
this.value = Math.Max(Math.Min(decimalValue, Maximum), Minimum);
break;
}

But you can use it to switch on a type, if all you have is a type:

switch (type) {
case Type intType when intType == typeof(int):
case Type decimalType when decimalType == typeof(decimal):
this.value = Math.Max(Math.Min(this.value, Maximum), Minimum);
break;
}

Note that this is not what the feature is intended for, it becomes less readable than a traditional if...else if...else if...else chain, and the traditional chain is what it compiles to anyway. I do not recommend using pattern matching like this.

Switch case on type c#

Update C# 7

Yes: Source

switch(shape)
{
case Circle c:
WriteLine($"circle with radius {c.Radius}");
break;
case Rectangle s when (s.Length == s.Height):
WriteLine($"{s.Length} x {s.Height} square");
break;
case Rectangle r:
WriteLine($"{r.Length} x {r.Height} rectangle");
break;
default:
WriteLine("<unknown shape>");
break;
case null:
throw new ArgumentNullException(nameof(shape));
}

Prior to C# 7

No.

http://blogs.msdn.com/b/peterhal/archive/2005/07/05/435760.aspx

We get a lot of requests for addditions to the C# language and today
I'm going to talk about one of the more common ones - switch on type.
Switch on type looks like a pretty useful and straightforward feature:
Add a switch-like construct which switches on the type of the
expression, rather than the value. This might look something like
this:

switch typeof(e) { 
case int: ... break;
case string: ... break;
case double: ... break;
default: ... break;
}

This kind of statement would be extremely useful for adding virtual
method like dispatch over a disjoint type hierarchy, or over a type
hierarchy containing types that you don't own. Seeing an example like
this, you could easily conclude that the feature would be
straightforward and useful. It might even get you thinking "Why don't
those #*&%$ lazy C# language designers just make my life easier and
add this simple, timesaving language feature?"

Unfortunately, like many 'simple' language features, type switch is
not as simple as it first appears. The troubles start when you look at
a more significant, and no less important, example like this:

class C {}
interface I {}
class D : C, I {}

switch typeof(e) {
case C: … break;
case I: … break;
default: … break;
}

Link: https://blogs.msdn.microsoft.com/peterhal/2005/07/05/many-questions-switch-on-type/

Switch expression on System.Type in C# 8

As others have alluded to, you actually need to have an instance of a type available to use the new type-matching features, not the representative System.Type. If you want to match directly on the type, the way you're doing it seems to be the only viable way for the time being.

That being said, I would argue in this case that a standard switch statement might be more readable:

switch (type)
{
case Type _ when type == typeof(string):
return str;

case Type _ when type == typeof(string[]):
return str.Split(',', ';');

default:
return TypeDescriptor.GetConverter(type).ConvertFromString(str);
}

If you really want to keep the switch expression, you could potentially get around this by matching on type names instead, though as a commenter pointed out below, this option is particularly brittle and will not work with certain types (such as DateTime? or Nullable<DateTime>):

public static object Convert(string str, Type type) =>
type.Name switch
{
nameof(string) => str,
nameof(string[]) => str.Split(new[] { ',', ';' }),
_ => TypeDescriptor.GetConverter(type).ConvertFromString(str)
};

C# switch with types

I'll answer the question exactly as asked: There is no way.

switch as of C# 6 only supports matching constants of certain types exactly. You are not trying to match constants. You are invoking the IsAssignableFrom method many times.

Note, that IsAssignableFrom is not identical to matching types exactly. Therefore, any solution based on equality comparisons or hash tables can't work.

I think the if ... else if solution that you have is totally fine.

Weird(?) behavior while using c# switch expression

The compiler needs to figure out a type for the switch expression. Its type can't be Base1, Base2 and BaseContainer at the same time, after all.

To do this, it finds the best common type of the expressions in each of switch expression's arms. According to the specification, the type is the same type as the type inferred when calling a generic method like this:

Tr M<X>(X x1 ... X xm)

with the expressions of the switch expression's arms passed as arguments.

I won't go into the details of type inference, but in general it is quite intuitive (as it is here). The best common type for new Base1(), new Base2(), and default(BaseContainer) is BaseContainer, and the best common type for new Base1(), (IBase)new Base2(), and default(BaseContainer) is IBase.

So if you don't cast, then the switch expression produces a BaseContainer, which means that the new Base1 object you created has to be converted to a BaseContainer first. This calls your implicit operator, which returns a BaseContainer object, and so GetType returns BaseContainer.

If you cast any of the arms to IBase, then the whole switch expression produces IBase. Converting from Base1 to IBase is a boxing conversion, and so doesn't change its dynamic type (i.e. what GetType returns).



Related Topics



Leave a reply



Submit