views:

12875

answers:

9

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?

+16  A: 

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
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
IDictionary is indeed a great solution for this matter, thx a lot
Sem Dendoncker
Polymorphism where approriate. If this "type" is used for serialization then you'd be mixing concerns.
Dave Van den Eynde
A: 

I'd use the string (Name) at the top of the switch:

  private int GetNodeType(NodeDTO node)
            {
                    switch (node.GetType().Name)
                    { 
                            case "CasusNodeDTO":
                                    return 1;
                                    break;
                            case "BucketNodeDTO":
                                    return 3;
                                    break;
                           // ...

                            default:
                                    return -1;
                                    break;
                    }
            }
Program.X
If you change the Type's Name during refactoring, you could introduce a bug that would not be caught until runtime.
Keith Sirmons
+6  A: 

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 ...

I suspect the latter might be a bit faster.

Ch00k
I second that, but I think comparing references is faster than repeated casting attempts.
Dave Van den Eynde
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
Ch00k
+2  A: 

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
+5  A: 

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
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
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
+2  A: 

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
+9  A: 

Here is some info why .net does not provide switching on types.

As usual - workarounds always exists.

This one ain't mine, but unfortunately i have lost source.
It makes switching on types possible but i personally think it's quite awkward (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; })
Arnis L.
+1  A: 

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?

bjax-bjax
A: 

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..

NIck