Использование Case/Switch и GetType для определения объекта

Возможный дубликат:
С# - Есть ли более эффективная альтернатива, чем это для типа включения?

Если вы хотите switch для типа объекта, что это лучший способ сделать это?

фрагмент кода

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;
    }
}

Я знаю, что это не работает, но мне было интересно, как вы могли это решить. Является ли выражение if/else подходящим в этом случае?

Или вы используете переключатель и добавляете .ToString() к типу?

Ответ 1

Если мне действительно приходилось switch по типу объекта, я бы использовал .ToString(). Однако я бы избегал этого любой ценой: IDictionary<Type, int> будет делать гораздо лучше, visitor может быть излишним, но в остальном это все равно идеально подходящее решение.

Ответ 2

Это не решит вашу проблему напрямую, так как вы хотите включить свои собственные пользовательские типы, но для других, которые хотят только встроить типы, вы можете использовать TypeCode перечисление:

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

    case TypeCode.Int32:
        // Handle Int32
        break;
     ...
}

Ответ 3

В сообщении блога MSDN Многие вопросы: тип включения - это информация о том, почему .NET не обеспечивает включение типов.

Как обычно - обходные пути всегда существуют.

Этот не мой, но, к сожалению, я потерял источник. Это делает возможным включение типов, но я лично считаю это довольно неудобным (идея словаря лучше):

  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;
      }
  }

Использование:

 new Switch(foo)
     .Case<Fizz>
         (action => { doingSomething = FirstMethodCall(); })
     .Case<Buzz>
         (action => { return false; })

Ответ 4

Я бы просто использовал оператор if. В этом случае:

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

Другой способ сделать это:

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

Первый пример верен только для точных типов, где последний также проверяет наследование.

Ответ 5

Я столкнулся с той же проблемой и наткнулся на этот пост. Это значит, что подход IDictionary:

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;
    }
}

Если это так, я не могу сказать, что я поклонник согласования чисел в словаре с утверждениями case.

Это было бы идеально, но словарная ссылка убивает его:

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;
    }
}

Есть ли другая реализация, которую я пропустил?

Ответ 6

Вы можете сделать это:

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

Хотя это было бы более элегантно, возможно, это не так эффективно, как некоторые другие ответы здесь.

Ответ 7

Вы можете сделать это:

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

Это ясно и легко. Это немного медленнее, чем кеширование словаря где-то... но для большого количества кода это не имеет значения.

Ответ 8

Один из подходов состоит в том, чтобы добавить чистый виртуальный метод GetNodeType() в NodeDTO и переопределить его в потомках, чтобы каждый потомок возвращал фактический тип.

Ответ 9

В зависимости от того, что вы делаете в инструкции switch, правильным ответом является полиморфизм. Просто поместите виртуальную функцию в интерфейс/базовый класс и переопределите для каждого типа node.

Ответ 10

Я думаю, что это лучше

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

                            default:
                                    return -1;
                                    break;
                    }
            }

Ответ 11

Я действительно предпочитаю подход, приведенный в качестве ответа здесь: Есть ли более эффективная альтернатива этому типу включения?

Однако есть хороший аргумент в том, что не следует применять какие-либо типы сравнения в объектно-ориентированном языке, таком как С#. Вы могли бы в качестве альтернативы расширить и добавить дополнительные требуемые функции, используя наследование.

Этот момент обсуждался в комментариях авторов блога здесь: http://blogs.msdn.com/b/jaredpar/archive/2008/05/16/switching-on-types.aspx#8553535

Я нашел это чрезвычайно интересным моментом, который изменил мой подход в подобной ситуации и только надеюсь, что это поможет другим.

С уважением, Уэйн