Использование 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
Я нашел это чрезвычайно интересным моментом, который изменил мой подход в подобной ситуации и только надеюсь, что это поможет другим.
С уважением, Уэйн