252

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:
Ola Ström
  • 4,136
  • 5
  • 22
  • 41
Adam
  • 2,523
  • 2
  • 15
  • 4
  • 2
    Or dynamic dispatch: http://stackoverflow.com/questions/3902496/c-call-non-generic-method-from-generic-method – Kirk Woll Dec 18 '10 at 14:35
  • Maybe see also: http://stackoverflow.com/questions/156467/switch-pattern-matching-idea – Marc Gravell Dec 18 '10 at 14:37
  • 3
    Can you describe *why* you are switching on the type of an object? That would help. Also, what are the relationships amongst the types? Do they all have a common base type (other than object)? Are they all class types? Is there ever a case where an object can fall into *two* of your type categories? (If two of them are interfaces and the object implements both, for instance.) Are the types ever nullable value types? Enums? Delegates? Is covariance of generic delegates and interfaces ever a factor? – Eric Lippert Dec 18 '10 at 14:47
  • 1
    see also http://stackoverflow.com/questions/156467/switch-pattern-matching-idea http://stackoverflow.com/questions/7252186/switch-case-on-type-c-sharp http://stackoverflow.com/questions/7542793/how-to-use-switch-case-on-a-type http://stackoverflow.com/questions/298976/c-sharp-is-there-a-better-alternative-than-this-to-switch-on-type – Mikhail Poda May 01 '12 at 11:51
  • http://stackoverflow.com/questions/94305/what-is-quicker-switch-on-string-or-elseif-on-type http://stackoverflow.com/questions/7149788/c-sharp-switch-on-object-type-at-runtime http://stackoverflow.com/questions/6304815/why-is-this-switch-on-type-case-considered-confusing http://stackoverflow.com/questions/5947343/how-to-switch-between-possible-type-of-an-object http://stackoverflow.com/questions/10115028/best-way-to-switch-behavior-based-on-type http://stackoverflow.com/questions/2551773/c-sharp-which-is-the-best-alternative-to-switch-on-type – Mikhail Poda May 01 '12 at 11:51
  • Curiously vb.net does allow it... – Rauland Sep 10 '15 at 10:36
  • 2
    C# 7 offers this now... Pattern Matching. Cool example reference: https://visualstudiomagazine.com/articles/2017/02/01/pattern-matching.aspx – TravisWhidden May 03 '18 at 18:25
  • 3
    Documentation of C# 7's pattern matching - https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/switch#type-pattern – foson Oct 03 '18 at 17:51
  • @Adam, could you update the chosen answer to be `gjvdkamp`'s answer, based on the new C# featues? – Brondahl Dec 01 '20 at 14:16
  • Just switch on myobj directly switch (myobj) { case MyType1: ... break; case MyType2: ... break; defauilt: throw new unsupported... } – Mark Lauter Aug 24 '21 at 04:04

5 Answers5

258

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.

Per Lundberg
  • 3,837
  • 1
  • 36
  • 46
gjvdkamp
  • 9,929
  • 3
  • 38
  • 46
  • 1
    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`. – IAbstract Dec 18 '10 at 15:12
  • 1
    In the case of testing for types, I would actually prefer testing with the `if-then` as I don't have to mess with strings. One simple if-then against @Mark's solution: `if (typeTests.Keys.Contains(TypeToTest))` and you have something probably comparable to performance with switch-case (because of the hashed keys) and not near as error prone, IMO. – IAbstract Dec 18 '10 at 15:32
  • I liked this method as a way to help generate moq models for testing – cyclical Jan 26 '17 at 16:24
  • 2
    Sorry, but the `ToString` seems like a bad plan - as renaming types using Visual Studio will break this. Why not `typeOf(Type1)`. – Ian Grainger May 03 '19 at 08:18
  • I'm calling it clumsy.. Right now the way to go is pattern matching, I'll update this answer – gjvdkamp May 03 '19 at 08:29
  • If you're worried about catching type renames as strings, you should be using `nameof(Type1)` instead of string literals. Actually, you should be doing that anyways _any_ time a string is really representing the name of a type or property. – brichins May 28 '19 at 20:49
  • 7
    This does not answer the question. The question was about switching on Type, not about switching on the type of an object. – N73k Jun 20 '19 at 19:10
  • 4
    The code example literally switches on the type of MyObj. – gjvdkamp Jun 20 '19 at 19:22
  • 2
    Could you update this to show how to switch on an actual `Type` instance? To have something like `switch (typeInstance) { case int: { ... } }` – Tomáš Hübelbauer Dec 08 '19 at 16:12
  • You can do that with normal overloading, have a method with different overloads for each type you want to handle. – gjvdkamp Dec 08 '19 at 16:17
  • 1
    How does this work with `public SomeGenericMethod() {switch (typeof(T) ...`? It just gives the compile error `An expression of type 'Type' cannot be handled by a pattern of type ...` – BurnsBA Aug 31 '20 at 00:12
  • So this was supposed to have been added in 7.0, correct? Because VS is throwing a compiler error telling me `Feature 'type pattern' is unavailable in 8.0` and that I'd need to use 9.0+ instead. – arkon Mar 23 '22 at 07:16
  • The Strategy pattern could also work in this context. – Scott Fraley Mar 30 '22 at 21:59
  • @BurnsBA see https://stackoverflow.com/a/299001 and https://stackoverflow.com/questions/44905 – n0099 Apr 25 '23 at 22:38
235

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.

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Mark H
  • 13,797
  • 4
  • 31
  • 45
  • 11
    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)](); ` – Automatico Feb 26 '13 at 23:05
  • 3
    This should be a very efficient/performant solution. Just note that this won't work with subclasses. – sparebytes Sep 10 '13 at 20:04
  • @YevgeniGrinberg I haven't tested this, but I'm certain that `typeof(object) != typeof(MyType)` even though MyType is a subclass of object. – sparebytes Sep 17 '13 at 16:21
  • 3
    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](http://stackoverflow.com/a/299120/787757) but its ussage is a little verbose – sparebytes Sep 17 '13 at 16:28
  • 5
    I would rather use the `Action action; if(@switch.TryGetValue(typeof(Type1), out action) action();` instead of searching twice.. (contains & indexers) – Jeroen van Langen Mar 10 '15 at 10:51
  • If you want to support sub-classes (or any other more complex requirement) simply write a custom `IEqualityComparer` and implement it's Equals method as desired - although I must confess writing a good GetHash could be difficult - of the top of my head I can't really think of any obvious hash implementation that supports equality of sub-classes... **Word of warning:** If you don't compare sub-classing both ways (up _and_ down the type hierarchy) you could get weird results that depend on the order in which types are added to the dictionary! (And technically it would be invalid equality.) – AnorZaken Apr 13 '16 at 10:47
  • There are new features of C# that allow this now. See this answer: https://stackoverflow.com/a/4478490/567547 – akousmata Jun 10 '20 at 13:47
30

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));
}
CarenRose
  • 1,266
  • 1
  • 12
  • 24
cdiggins
  • 17,602
  • 7
  • 105
  • 102
3

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

string fullName = typeof(MyObj).FullName;

switch (fullName)
{
    case "fullName1":
    case "fullName2":
    case "fullName3":
}
Ghyath Serhal
  • 7,466
  • 6
  • 44
  • 60
  • 32
    This approach is fragile if you rename/move any of the classes. – Lee Dec 18 '10 at 15:01
  • 33
    A little refactoring and you are screwed. – weismat Jun 16 '11 at 06:32
  • Yeah, but I can not add: case typeof(Bitmap).FullName: – Pedro77 Jul 18 '13 at 22:31
  • 20
    This is less fragile in C# 6.0 using nameof: case nameof() – Josh Feb 26 '16 at 20:47
  • @Josh - Can you please give an example of how to write this? As far as I am aware, this would only give you the name of the variable, not the class name? – michaelmsm89 Mar 18 '16 at 20:29
  • 1
    You would use nameof() instead of nameof(). 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. – Josh Mar 31 '16 at 18:28
  • 6
    Why not use case `case nameof(MyClass): break; ` so that it is more refactorable – Dživo Jelić Nov 23 '19 at 00:49
  • @Dzivo The switch construct in C# only accepts literals as its cases. – dynamichael Feb 05 '22 at 05:22
  • 1
    @dynamichael https://stackoverflow.com/questions/30097705/is-it-possible-to-use-an-nameof-expression-in-switch-statement i just tried it using c# 9.0 and .net 6.0 maybe some older versions dont support it ? – Dživo Jelić Feb 08 '22 at 13:46
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
}
IAbstract
  • 19,551
  • 15
  • 98
  • 146