使用 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 &gt; 75 case &gt; 50, case &gt; 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&lt;Type, int&gt; 会做得更好,visitor 可能是矫枉过正,但否则它仍然是一个完美的解决方案。

【讨论】:

IDictionary 在我看来是一个很好的解决方案。如果要测试的类型超过一两种,我通常会使用它。好吧,或者首先使用多态性来避免切换类型。 适当的多态性。如果此“类型”用于序列化,那么您将混淆问题。 为什么不努力并举例说明IDictionary在上述情况下的应用?

以上是关于使用 Case/Switch 和 GetType 来确定对象 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

我可以使用带有两个变量的 case/switch 语句吗?

case/switch 语句的 Python 等效项是啥? [复制]

switch...case语句嵌套问题

c语言中CASE 问题?

使用 GetType() 和 typeof() 获取 Type 有啥区别? [复制]

Smack 中的 Presence getType() 和 isAvailable() 有啥区别?