270

Possible Duplicate:
C# - Is there a better alternative than this to 'switch on type'?

C# doesn't support switching on the type of an object.
What is the best pattern of simulating this:

switch (typeof(MyObj)) case Type1: case Type2: case Type3: 
10

5 Answers 5

297

Update:

This got fixed in C# 7.0 with pattern matching

switch (MyObj) { case Type1 t1: case Type2 t2: case Type3 t3: } 

Old answer:

It is a hole in C#'s game, no silver bullet yet.

You should google on the 'visitor pattern' but it might be a little heavy for you but still something you should know about.

Here's another take on the matter using Linq: http://community.bartdesmet.net/blogs/bart/archive/2008/03/30/a-functional-c-type-switch.aspx

Otherwise something along these lines could help

// nasty.. switch(MyObj.GetType().ToString()){ case "Type1": etc } // clumsy... if myObj is Type1 then if myObj is Type2 then 

etc.

Sign up to request clarification or add additional context in comments.

14 Comments

The problem with the functional c-type switch is that it is not getting the precompiled speed that the actual switch-case syntax gets. This can lull the programmer into overusing this switch- look-alike class thinking that it gets the same advantages. This looks like a glamorous wrapper for a more expensive if-then-with-lambdas.
Sorry, but the ToString seems like a bad plan - as renaming types using Visual Studio will break this. Why not typeOf(Type1).
This does not answer the question. The question was about switching on Type, not about switching on the type of an object.
The code example literally switches on the type of MyObj.
Could you update this to show how to switch on an actual Type instance? To have something like switch (typeInstance) { case int: { ... } }
|
237

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.

7 Comments

You should probably do a check if that type is in the dictionary. Could do so fairly simple like this if(@switch.ContainsKey(typeof(MyType))) @switch[typeof(MyType)]();
This should be a very efficient/performant solution. Just note that this won't work with subclasses.
@YevgeniGrinberg I haven't tested this, but I'm certain that typeof(object) != typeof(MyType) even though MyType is a subclass of object.
If you want a solution that works with sub-types, then at some point IsAssignableFrom will have to be used to make the comparison. This answer supports sub-types but its ussage is a little verbose
I would rather use the Action action; if(@switch.TryGetValue(typeof(Type1), out action) action(); instead of searching twice.. (contains & indexers)
|
29

There is a simple answer to this question which uses a dictionary of types to look up a lambda function. Here is how it might be used:

var ts = new TypeSwitch() .Case((int x) => Console.WriteLine("int")) .Case((bool x) => Console.WriteLine("bool")) .Case((string x) => Console.WriteLine("string")); ts.Switch(42); ts.Switch(false); ts.Switch("hello"); 

There is also a generalized solution to this problem in terms of pattern matching (both types and run-time checked conditions):

var getRentPrice = new PatternMatcher<int>() .Case<MotorCycle>(bike => 100 + bike.Cylinders * 10) .Case<Bicycle>(30) .Case<Car>(car => car.EngineType == EngineType.Diesel, car => 220 + car.Doors * 20) .Case<Car>(car => car.EngineType == EngineType.Gasoline, car => 200 + car.Doors * 20) .Default(0); var vehicles = new object[] { new Car { EngineType = EngineType.Diesel, Doors = 2 }, new Car { EngineType = EngineType.Diesel, Doors = 4 }, new Car { EngineType = EngineType.Gasoline, Doors = 3 }, new Car { EngineType = EngineType.Gasoline, Doors = 5 }, new Bicycle(), new MotorCycle { Cylinders = 2 }, new MotorCycle { Cylinders = 3 }, }; foreach (var v in vehicles) { Console.WriteLine("Vehicle of type {0} costs {1} to rent", v.GetType(), getRentPrice.Match(v)); } 

Comments

3

I have used this form of switch-case on rare occasion. Even then I have found another way to do what I wanted. If you find that this is the only way to accomplish what you need, I would recommend @Mark H's solution.

If this is intended to be a sort of factory creation decision process, there are better ways to do it. Otherwise, I really can't see why you want to use the switch on a type.

Here is a little example expanding on Mark's solution. I think it is a great way to work with types:

Dictionary<Type, Action> typeTests; public ClassCtor() { typeTests = new Dictionary<Type, Action> (); typeTests[typeof(int)] = () => DoIntegerStuff(); typeTests[typeof(string)] = () => DoStringStuff(); typeTests[typeof(bool)] = () => DoBooleanStuff(); } private void DoBooleanStuff() { //do stuff } private void DoStringStuff() { //do stuff } private void DoIntegerStuff() { //do stuff } public Action CheckTypeAction(Type TypeToTest) { if (typeTests.Keys.Contains(TypeToTest)) return typeTests[TypeToTest]; return null; // or some other Action delegate } 

Comments

2

I did it one time with a workaround, hope it helps.

string fullName = typeof(MyObj).FullName; switch (fullName) { case "fullName1": case "fullName2": case "fullName3": } 

9 Comments

This approach is fragile if you rename/move any of the classes.
A little refactoring and you are screwed.
This is less fragile in C# 6.0 using nameof: case nameof(<classname>)
You would use nameof(<classname>) instead of nameof(<variablename>). For instance, in the example above, nameof(MyObj). However I was mistaken as this is switching on the full name, whereas nameof will only give you the class name.
Why not use case case nameof(MyClass): break; so that it is more refactorable
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.