192

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

If you want to switch on a type of object, what is the best way to do this?

Code snippet

private int GetNodeType(NodeDTO node)
{
    switch (node.GetType())
    { 
        case typeof(CasusNodeDTO):
            return 1;
        case typeof(BucketNodeDTO):
            return 3;
        case typeof(BranchNodeDTO):
            return 0;
        case typeof(LeafNodeDTO):
            return 2;
        default:
            return -1;
    }
}

I know this doesn't work that way, but I was wondering how you could solve this. Is an if/else statement appropriate in this case?

Or do you use the switch and add .ToString() to the type?

Community
  • 1
  • 1
user29964
  • 15,740
  • 21
  • 56
  • 63
  • 20
    If anyone's interested, Peter Hallam discusses why this is not a feature of C# at http://blogs.msdn.com/peterhal/archive/2005/07/05/435760.aspx – sourcenouveau Jan 25 '10 at 16:50
  • 2
    I know this is 2017 and this is an old comment however...Having just read that article by Peter Hallam I'm now confused. C#7 allows switching where the order of the case statements is important - surely this is in conflict for what seems to be one of his main reasons it's not been added to the language? – Wayne Feltham Sep 07 '17 at 11:42
  • 13
    You can actually switch on types in c# 7...I guess they changed their mind (or figured out a nicer way to do it) after 12 years: https://stackoverflow.com/questions/298976/is-there-a-better-alternative-than-this-to-switch-on-type/299001#299001 – drojf Aug 30 '18 at 10:17
  • 1
    A related remark: VB.NET has this functionality built-in. – Mike Mar 18 '19 at 09:07
  • Yes, it seems like that should work. You'd think the typeof( )s would be resolved at compile time and therefore yield a constant for the run-time to switch-on but alas not. Not yet anyway. :( – Zeek2 Sep 22 '20 at 13:57
  • Twelve years later this is a funny thread, almost only of historical meaning, unless someone sticky to C# < 7. I tried to change tag from C# to C# 6.0 but I couldn't. – Marcelo Scofano Diniz Sep 20 '21 at 17:49

10 Answers10

180

This won't directly solve your problem as you want to switch on your own user-defined types, but for the benefit of others who only want to switch on built-in types, you can use the TypeCode enumeration:

switch (Type.GetTypeCode(node.GetType()))
{
    case TypeCode.Decimal:
        // Handle Decimal
        break;

    case TypeCode.Int32:
        // Handle Int32
        break;
     ...
}
splattne
  • 102,760
  • 52
  • 202
  • 249
Ashley
  • 2,108
  • 1
  • 14
  • 12
  • 3
    Nice idea, but doesn't seem to work for user defined classes. – Samik R Dec 08 '11 at 18:23
  • 1
    No, everything else will just give 'Object' back. – Ashley Dec 08 '11 at 22:45
  • @splattne - Just curious, why exactly was there any need to edit the indentation? – Ashley Sep 19 '13 at 23:53
  • @Ashley I fixed the snippet because the "..." weren't part of the code block. See: http://imgur.com/CfTIzTU - Fixing the indentation was a by-product. :-) – splattne Sep 20 '13 at 10:03
  • 4
    @splattne The '...' wasn't supposed to be part of the code because '...' is not, in fact, code. I could see an argument for making it easier to read, though. The indentation however... I don't know how you can call it 'fixing', just because it's how you like it now. I don't see any StackOverflow guidelines for how to indent code. There's a whole variety of styles in this question alone. – Ashley Sep 20 '13 at 13:08
  • it's quite good for primitive types, but when yo have arrays or other complex types, you have to go with if/else – dmihailescu Feb 13 '15 at 17:15
  • Yes, but sometimes it is what you need. I just now write a chart library integration. Data comes from a Series and must be one of the primitives - OR a struct that then contains primitives as properties. GetTypeCode is "all I need". NICE. – TomTom Mar 27 '16 at 14:39
  • Not work for `enum` – huang Nov 07 '19 at 13:11
  • Like told in the asnwer, it works for built in types only. It's the solution I was seeking for. Thank you @Ashley. –  May 03 '20 at 12:09
  • In newer version of C# i guess C#7+ there is a better alternative https://systemoutofmemory.com/blogs/the-programmer-blog/c-sharp-switch-on-type – GPuri Aug 24 '22 at 08:22
98

If I really had to switch on type of object, I'd use .ToString(). However, I would avoid it at all costs: IDictionary<Type, int> will do much better, visitor might be an overkill but otherwise it is still a perfectly fine solution.

Anton Gogolev
  • 113,561
  • 39
  • 200
  • 288
  • IDictionary is a fine solution in my opinion. If it's more than one or two types to test, I'd usually use that. Well, or just use polymorphism in the first place to avoid switching on types. – OregonGhost Apr 02 '09 at 09:12
  • Polymorphism where approriate. If this "type" is used for serialization then you'd be mixing concerns. – Dave Van den Eynde Apr 02 '09 at 09:27
  • 11
    why not make the effort and give an example for the application of IDictionary in the stated case? – jasie May 11 '21 at 06:21
49

In the MSDN blog post Many Questions: switch on type is some information on why .NET does not provide switching on types.

As usual - workarounds always exists.

This one isn't mine, but unfortunately I have lost the source. It makes switching on types possible, but I personally think it's quite awkward (the dictionary idea is better):

  public class Switch
  {
      public Switch(Object o)
      {
          Object = o;
      }

      public Object Object { get; private set; }
  }


  /// <summary>
  /// Extensions, because otherwise casing fails on Switch==null
  /// </summary>
  public static class SwitchExtensions
  {
      public static Switch Case<T>(this Switch s, Action<T> a)
            where T : class
      {
          return Case(s, o => true, a, false);
      }

      public static Switch Case<T>(this Switch s, Action<T> a,
           bool fallThrough) where T : class
      {
          return Case(s, o => true, a, fallThrough);
      }

      public static Switch Case<T>(this Switch s,
          Func<T, bool> c, Action<T> a) where T : class
      {
          return Case(s, c, a, false);
      }

      public static Switch Case<T>(this Switch s,
          Func<T, bool> c, Action<T> a, bool fallThrough) where T : class
      {
          if (s == null)
          {
              return null;
          }

          T t = s.Object as T;
          if (t != null)
          {
              if (c(t))
              {
                  a(t);
                  return fallThrough ? s : null;
              }
          }

          return s;
      }
  }

Usage:

 new Switch(foo)
     .Case<Fizz>
         (action => { doingSomething = FirstMethodCall(); })
     .Case<Buzz>
         (action => { return false; })
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Arnis Lapsa
  • 45,880
  • 29
  • 115
  • 195
  • 8
    Pretty cool although this is a fairly expensive pattern that leads to a relatively large amount of time in GC. But still, very readable... – JoeGeeky Nov 02 '10 at 09:50
  • 1
    The article states *"Programmers would be extremely surprised to learn that reordering the case labels had an affect on which case was chosen."* I couldn't disagree more. Imagine colouring a fuel gauge green/orange/red, you'd do a `switch percentageFuelRemaining` then `case > 75` `case > 50`, `case > 25`. – NibblyPig Oct 07 '15 at 10:06
  • Cool solution but I'd only use it once not regularly during program flow. Reflection is expensive. Great for handling multiple exceptions and reporting errors etc but if you are using it hundreds of times then it is a poor solution. – rollsch Oct 30 '16 at 22:35
35

I'm faced with the same problem and came across this post. Is this what's meant by the IDictionary approach:

Dictionary<Type, int> typeDict = new Dictionary<Type, int>
{
    {typeof(int),0},
    {typeof(string),1},
    {typeof(MyClass),2}
};

void Foo(object o)
{
    switch (typeDict[o.GetType()])
    {
        case 0:
            Print("I'm a number.");
            break;
        case 1:
            Print("I'm a text.");
            break;
        case 2:
            Print("I'm classy.");
            break;
        default:
            break;
    }
}

If so, I can't say I'm a fan of reconciling the numbers in the dictionary with the case statements.

This would be ideal but the dictionary reference kills it:

void FantasyFoo(object o)
{
    switch (typeDict[o.GetType()])
    {
        case typeDict[typeof(int)]:
            Print("I'm a number.");
            break;
        case typeDict[typeof(string)]:
            Print("I'm a text.");
            break;
        case typeDict[typeof(MyClass)]:
            Print("I'm classy.");
            break;
        default:
            break;
    }
}

Is there another implementation I've overlooked?

bjaxbjax
  • 1,351
  • 2
  • 15
  • 24
  • 3
    Perhaps you could create an enumeration that would take the place of int in your type dictionary? This should alleviate your code of those pesky magic numbers. – trod Jun 26 '17 at 05:43
31

I'd just use an if statement. In this case:

Type nodeType = node.GetType();
if (nodeType == typeof(CasusNodeDTO))
{
}
else ... 

The other way to do this is:

if (node is CasusNodeDTO)
{
}
else ...

The first example is true for exact types only, where the latter checks for inheritance too.

annemartijn
  • 1,538
  • 1
  • 23
  • 45
David Wengier
  • 10,061
  • 5
  • 39
  • 43
  • I second that, but I think comparing references is faster than repeated casting attempts. – Dave Van den Eynde Apr 02 '09 at 09:15
  • I'm not sure its comparing references though. I think the RuntimeType system comes into effect. I'm just guessing though, because if it wasn't something like that, the compiler wouldn't tell you that typeof(X) is not a constant – David Wengier Apr 02 '09 at 10:09
  • 3
    the second type check with is IS slower because it checks the whole class hierarchy. – msfanboy Aug 12 '11 at 22:44
21

You can do this:

function void PrintType(Type t) {
 var t = true;
 new Dictionary<Type, Action>{
   {typeof(bool), () => Console.WriteLine("bool")},
   {typeof(int),  () => Console.WriteLine("int")}
 }[t.GetType()]();
}

It's clear and its easy. It a bit slower than caching the dictionary somewhere.. but for lots of code this won't matter anyway..

nreyntje
  • 505
  • 6
  • 3
  • 2
    Anyone care to comment on why this got downvoted? What about it is either incorrect or does not perform well? – Norman H Feb 14 '12 at 15:49
  • I don't think I would do that, but only for esthetical reasons (a bit silly really). That said, I like to see people think outside the box and it is a cool use of lambdas :) – LOAS Mar 08 '12 at 09:16
  • 5
    This is an elegant solution that is efficient for large numbers of types and would clearly convey the authors intentions. – Rupert Rawnsley Mar 30 '14 at 12:21
  • This is the cleanest so far solution given for this question – Vasil Popov May 30 '19 at 12:17
  • A bit naive but concise solution. If I had been the OP, I would have accepted this as the answer because...well...I like lambdas :P – gvdm Nov 28 '19 at 14:06
14

You can do this:

if (node is CasusNodeDTO)
{
    ...
}
else if (node is BucketNodeDTO)
{
    ...
}
...

While that would be more elegant, it's possibly not as efficient as some of the other answers here.

Dave Van den Eynde
  • 17,020
  • 7
  • 59
  • 90
  • After doing some performance tests i would completely agree that using an if else is the best option for these types of checks,using a continuous method calling is pretty much bad, as they will never stop even if the match is found very early (unless you throw an exception that will stop the other methods from calling, but still very bad usage) – Ahmed Fwela Nov 21 '16 at 15:25
  • top this up with the new C# syntax that allows you to cast with the `is` keyword to get the properly typed variable, and this is the best answer – knocte Apr 08 '22 at 03:03
7

One approach is to add a pure virtual GetNodeType() method to NodeDTO and override it in the descendants so that each descendant returns actual type.

sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • While that's the OO way to handle it, you might decide that Node shouldn't have to support any of this. – Dave Van den Eynde Apr 02 '09 at 09:16
  • A big +1 here and to Jason Coyne. Has no one else read the book Refactoring? This is a textbook example: http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html – TrueWill Aug 04 '10 at 01:14
5

Depending on what you are doing in the switch statement, the correct answer is polymorphism. Just put a virtual function in the interface/base class and override for each node type.

Jason Coyne
  • 6,509
  • 8
  • 40
  • 70
1

I actually prefer the approach given as the answer here: Is there a better alternative than this to 'switch on type'?

There is however a good argument about not implementing any type comparison methids in an object oriented language like C#. You could as an alternative extend and add extra required functionality using inheritance.

This point was discussed in the comments of the authors blog here: http://blogs.msdn.com/b/jaredpar/archive/2008/05/16/switching-on-types.aspx#8553535

I found this an extremely interesting point which changed my approach in a similar situation and only hope this helps others.

Kind Regards, Wayne

Community
  • 1
  • 1
Wayne Phipps
  • 2,019
  • 6
  • 26
  • 31