使用 Case/Switch 和 GetType 来确定对象 [重复]
Posted
技术标签:
【中文标题】使用 Case/Switch 和 GetType 来确定对象 [重复]【英文标题】:Using Case/Switch and GetType to determine the object [duplicate] 【发布时间】:2010-10-17 01:44:14 【问题描述】:可能重复:C# - Is there a better alternative than this to ‘switch on type’?
如果您想在一种对象上switch
,最好的方法是什么?
代码 sn-p
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()
添加到类型?
【问题讨论】:
如果有人感兴趣,Peter Hallam 会在blogs.msdn.com/peterhal/archive/2005/07/05/435760.aspx@ 讨论为什么这不是 C# 的一个特性 我知道这是 2017 年,但这是一条旧评论……刚刚阅读了 Peter Hallam 的那篇文章,我现在很困惑。 C#7 允许在 case 语句的顺序很重要的地方切换——这肯定是冲突的,因为这似乎是他没有被添加到语言中的主要原因之一? 您实际上可以在 c# 7 中打开类型...我猜他们在 12 年后改变了主意(或想出了更好的方法):***.com/questions/298976/… 相关说明:VB.NET内置了这个功能。 是的,看起来应该可以。你会认为 typeof( )s 会在编译时被解析,因此会为运行时生成一个常量来打开,但可惜不是。反正还没有。 :( 【参考方案1】:我只会使用 if 语句。在这种情况下:
Type nodeType = node.GetType();
if (nodeType == typeof(CasusNodeDTO))
else ...
另一种方法是:
if (node is CasusNodeDTO)
else ...
第一个示例仅适用于精确类型,后者也检查继承。
【讨论】:
我同意这一点,但我认为比较参考文献比重复投射尝试更快。 我不确定它的比较参考。我认为 RuntimeType 系统开始生效。不过我只是在猜测,因为如果不是那样的话,编译器不会告诉你 typeof(X) 不是常量 第二种类型检查速度较慢,因为它检查整个类层次结构。【参考方案2】:在 MSDN 博客文章 Many Questions: switch on type 中提供了一些关于为什么 .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; )
【讨论】:
相当酷,虽然这是一种相当昂贵的模式,会导致相对大量的 GC 时间。但仍然,非常可读...... 文章指出“程序员会非常惊讶地发现,重新排序案例标签会影响选择的案例。” 我完全不同意。想象一下将电量计涂成绿色/橙色/红色,你会先用switch percentageFuelRemaining
然后case > 75
case > 50
, case > 25
。
很酷的解决方案,但我只会在程序流程中不定期使用它一次。反射是昂贵的。非常适合处理多个异常和报告错误等,但如果您使用它数百次,那么它是一个糟糕的解决方案。【参考方案3】:
这不会直接解决您的问题,因为您想打开自己的用户定义类型,但为了其他只想打开内置类型的人的利益,您可以使用TypeCode 枚举:
switch (Type.GetTypeCode(node.GetType()))
case TypeCode.Decimal:
// Handle Decimal
break;
case TypeCode.Int32:
// Handle Int32
break;
...
【讨论】:
好主意,但似乎不适用于用户定义的类。 不,其他一切都只会返回“对象”。 @splattne - 只是好奇,为什么需要编辑缩进? @Ashley 我修复了 sn-p 因为“...”不是代码块的一部分。请参阅:imgur.com/CfTIzTU - 修复缩进是副产品。 :-) @splattne '...' 不应该是代码的一部分,因为 '...' 实际上不是代码。不过,我可以看到一个让它更容易阅读的论点。然而,缩进......我不知道你怎么能称之为“修复”,只是因为你现在喜欢它。我没有看到任何关于如何缩进代码的 *** 指南。仅在这个问题中就有各种各样的风格。【参考方案4】:我实际上更喜欢这里给出的答案: Is there a better alternative than this to 'switch on type'?
然而,关于在 C# 等面向对象的语言中不实现任何类型比较方法,这是一个很好的论据。作为替代方案,您可以使用继承来扩展和添加额外的必需功能。
作者博客的 cmets 讨论了这一点: http://blogs.msdn.com/b/jaredpar/archive/2008/05/16/switching-on-types.aspx#8553535
我发现这是一个非常有趣的观点,它改变了我在类似情况下的方法,只希望这对其他人有所帮助。
亲切的问候,韦恩
【讨论】:
【参考方案5】:你可以这样做:
function void PrintType(Type t)
var t = true;
new Dictionary<Type, Action>
typeof(bool), () => Console.WriteLine("bool"),
typeof(int), () => Console.WriteLine("int")
[t.GetType()]();
很清楚也很容易。 它比在某处缓存字典要慢一些..但是对于很多代码来说,这无论如何都无关紧要..
【讨论】:
有人愿意评论为什么这被否决了吗?不正确或表现不佳怎么办? 我不认为我会这样做,但只是出于审美原因(真的有点傻)。也就是说,我喜欢看到人们跳出框框思考,这是对 lambda 的一种很酷的使用 :) 这是一个优雅的解决方案,对大量类型有效,并且可以清楚地传达作者的意图。 这是迄今为止为这个问题提供的最干净的解决方案 有点幼稚但简洁的解决方案。如果我是 OP,我会接受这个作为答案,因为......嗯......我喜欢 lambdas :P【参考方案6】:我遇到了同样的问题并遇到了这篇文章。 这就是 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;
如果是这样,我不能说我喜欢将字典中的数字与案例陈述相协调。
这将是理想的,但字典参考杀死了它:
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;
还有其他我忽略的实现吗?
【讨论】:
也许您可以创建一个枚举来代替您的类型字典中的 int?这应该可以减轻那些讨厌的魔法数字的代码。【参考方案7】:根据你在 switch 语句中所做的事情,正确的答案是多态性。只需在接口/基类中放置一个虚函数并覆盖每个节点类型。
【讨论】:
【参考方案8】:一种方法是向 NodeDTO 添加纯虚拟 GetNodeType() 方法并在后代中覆盖它,以便每个后代返回实际类型。
【讨论】:
虽然这是处理它的 OO 方式,但您可能会认为 Node 不应该支持任何这些。 这里给 Jason Coyne 一个大 +1。没有人读过重构这本书吗?这是一个教科书示例:refactoring.com/catalog/replaceConditionalWithPolymorphism.html【参考方案9】:你可以这样做:
if (node is CasusNodeDTO)
...
else if (node is BucketNodeDTO)
...
...
虽然这会更优雅,但它可能不如这里的其他一些答案那么有效。
【讨论】:
在做一些性能测试后,我完全同意使用 if else 是这些类型检查的最佳选择,使用连续方法调用非常糟糕,因为即使很早就找到了匹配项(除非你抛出一个异常会阻止其他方法调用,但仍然非常糟糕的使用)【参考方案10】:如果我真的必须在对象类型上使用switch
,我会使用.ToString()
。但是,我会不惜一切代价避免它:IDictionary<Type, int>
会做得更好,visitor 可能是矫枉过正,但否则它仍然是一个完美的解决方案。
【讨论】:
IDictionary 在我看来是一个很好的解决方案。如果要测试的类型超过一两种,我通常会使用它。好吧,或者首先使用多态性来避免切换类型。 适当的多态性。如果此“类型”用于序列化,那么您将混淆问题。 为什么不努力并举例说明IDictionary在上述情况下的应用?以上是关于使用 Case/Switch 和 GetType 来确定对象 [重复]的主要内容,如果未能解决你的问题,请参考以下文章
case/switch 语句的 Python 等效项是啥? [复制]