【问题标题】:Why doesn't C# switch statement allow using typeof/GetType()?为什么 C# switch 语句不允许使用 typeof/GetType()?
【发布时间】:2009-11-10 20:34:58
【问题描述】:

如本例所示:

switch ( myObj.GetType ( ) )
{
    case typeof(MyObject):
        Console.WriteLine ( "MyObject is here" );
        break;
}

【问题讨论】:

  • @RHSeeger:Petter Hallam 的博文中没有任何声明可以解释为“人们太愚蠢了,无法理解这个概念”,也没有语言设计者谨慎行事,因为在世界。
  • 这与人们“太愚蠢”没有任何关系。 Peter 的观点是人们会发现这种行为令人惊讶,而不是难以理解。 C# 已经过精心设计,尽可能不让人感到意外。
  • 如果您曾经想编写一个switch 语句来切换对象的类型,那么您确实需要重构并将案例委托给对象实现。在一个设计良好的 OO 系统中,应该从不需要这样做。
  • 请注意,打开对象类型是一种强烈的代码气味。打开一个类型来确定要执行什么代码/调用哪个方法就像说“我希望我在这里遇到的所有对象都有一个我现在可以调用的方法”。所以一个更好的问题可能是“为什么我必须打开对象类型?”当您回答该问题时,请解决问题而不是症状:)

标签: c# .net reflection switch-statement


【解决方案1】:

在彼得哈勒姆的帖子中排名第二;这是一个很好的解释。

不过,您可以使用 TypeCode 来处理简单类型。

switch (Type.GetTypeCode(myObj.GetType())) {
    case TypeCode.Boolean: ...
    case TypeCode.Char: ...
    case TypeCode.String: ...
    case TypeCode.Object: ...
    default: ...
} 

【讨论】:

  • 知道如何测试 Emums 吗?
【解决方案2】:

我想在彼得的出色分析中添加以下想法:

从根本上说,“转换”的目的是选择一些不同的可能性中的一种。枚举、整数、布尔或字符串类型的给定值只能是一个值,因此在这样的值上“切换”是有意义的。但是类型是根本不同的。一个给定的值通常有 许多 类型。类型经常重叠。提议的“类型开关”与开关构造的声明用途不匹配。

【讨论】:

  • 谢谢埃里克,一个很好的答案。这里有人说vb.net可以做到这一点。它是如何工作的?它是对 vb.net 性能的妥协还是只是与 C# switch 语句不同的构造?
  • VB 构造的文档在这里:msdn.microsoft.com/en-us/library/cy37t14y.aspx
  • 谢谢埃里克,只是假设这里的海报是正确的。我现在明白了。
  • link above,对于 testexpression,VB 构造的文档说:“必需。表达式。必须计算为基本数据类型之一(布尔值字节字符日期双精度十进制 , Integer, Long, Object, SByte, Short, Single StringUIntegerULongUShort)。”因此,与 C# 一样,VB.NET 不允许任何东西作为 Select...Case 语句的测试表达式。
  • “一个给定的值通常有很多类型”——这根本没有任何意义。 .NET 中的每个值都只有 one 类型。该类型继承自什么,是一个基类,作为泛型参数,是完全不相关的。
【解决方案3】:

问题在于switch(根据规范)仅适用于原语(int 等)和字符串。但是,是的,那会很好to have F#-style matching

从第 8.7.2 节开始:

switch-label:
   case   constant-expression   :
   default   :

... switch 语句的控制类型由 switch 表达式确定。 如果 switch 表达式的类型是 sbyte, byte, short, ushort, int, uint, long, ulong、char、string 或 enum 类型,那么这是开关的控制类型 陈述。否则,必须存在一个用户定义的隐式转换(第 6.4 节) 将 switch 表达式的类型转换为以下可能的控制类型之一: sbyte,字节,短,ushort,int,uint,long,ulong,char,字符串。如果没有这样的隐含 存在转换,或者如果存在多个这样的隐式转换,则编译时 发生错误。

然而,很明显,使用这样一个受限集可以实现简单(高效)的 IL。请注意,string 是通过字典映射到整数来处理的。

【讨论】:

  • 谢谢马克。如果他们使用字典处理字符串,为什么 MS 不使用字典对其他类型做同样的事情?
  • vb.net 允许在开关中使用任何东西
  • @joan 因为它仅适用于编译时常量(例如字符串文字)。密钥在 IL 代码中使用,因此需要在编译时知道
  • @Pondidum - 我希望 VB.Net 开关仅在 IL 中实现一系列 If。如果您单步执行,您是否看到代码在每个“案例”处停止,而不是立即跳转到正确的案例?
  • @RuneFS typeof(SomeType) 形式的表达式也是常量,但不允许使用。也许typeof(T)typeof(SomeType<T>)(泛型)仍然会被禁止,因为在编译时无法检查唯一性,但除此之外,任何类型都应该是合法的。
【解决方案4】:

你可以这样做

switch ( myObj.GetType().Name )
{
    case "MyObject":
        Console.WriteLine ( "MyObject is here" );
        break;
}

这是可行的,因为切换仅适用于原始类型(正如其他人所说)。

【讨论】:

  • 这怎么行,因为在编译时值还是未知的!
  • @Philip:case 在编译时是已知的,因为在这种情况下它们被硬编码为字符串。这不是最佳解决方案,但确实有效。
【解决方案5】:

有一个good blog post on MSDN by Peter Hallam 解释了打开非常数值的问题。

"案例标签的顺序变为 在确定哪个块方面很重要 的代码被执行。因为 案例标签表达式不是 编译器无法验证的常量 案例标签的值是 不同,所以这是一种可能性 必须迎合。这运行 违背大多数程序员的直觉 关于一对中的 switch 语句 的方式。大多数程序员会 惊讶地发现,改变 他们的案例块的顺序改变了 他们节目的意义。转动它 周围,​​如果 打开的表达式是相等的 到 case 标签中的表达式,但是 控制没有转到那个标签。”

【讨论】:

  • 除了可以确定 typeof() 表达式的唯一性很好(泛型除外),那么顺序无关紧要。
【解决方案6】:

就是typeof不是常量,case必须是常量。

【讨论】:

    【解决方案7】:

    C# 中的开关仅适用于整数或字符串。 myObj.GetType() 返回一个 Type,它既不是整数也不是字符串。

    【讨论】:

      【解决方案8】:

      在 C# 7.0 中,您可以做到这一点。看 Pattern Matching in C# 7.0 Case Blocks

      // ----- Assume that spaceItem is of type SpaceType,
      //       and that Planet and Star derive from SpaceType.
      switch (spaceItem)
      {
        case Planet p:
          if (p.Type != PlanetType.GasGiant)
            LandSpacecraft(p);
          break;
        case Star s:
          AvoidHeatSource(s);
          break;
        case null:
          // ----- If spaceItem is null, processing falls here,
          //       even if it is a Planet or Star null instance.
          break;
        default:
          // ----- Anything else that is not Planet, Star, or null.
          break;
      }
      

      【讨论】:

      • 这是现代 C# 的正确答案。几乎所有受支持的 .NET 实现都支持 C# 7。
      【解决方案9】:

      你为什么不直接tostring()呢?

      【讨论】:

      • @PhilipWallace Marc 的回答与ToString() 方法有什么关系?
      【解决方案10】:

      除了懒惰之外,MS 没有充分的理由不实现类型切换。

      字符串切换是使用“if(..Equals(..))”s 来完成的,在少数情况下使用字典,在很多情况下使用字典。这两种方法都是为所有 .NET 类型定义的,因为 System.Object 具有虚拟的 Equals 和 GetHashCode。

      有人可能会说,“switch 可以使用覆盖 Equals 和 GetHashCode 的任何类型的表达式”,它会自动限定字符串、Type 等。是的,糟糕的 Equals/GetHashCode 实现会破坏 switch 语句,但是,嘿,你还可以破坏“==”运算符、“foreach”循环和一堆其他东西,所以我并没有真正看到 switch 被程序员的错误破坏的“大问题”。但即使他们不想对所有类型都允许它,无论出于何种原因,Type 肯定是安全的,因为 Type.Equals() 是定义良好的,并且 GetHashCode 也已实现。

      另外,我不同意你考虑继承的论点; switch 转到常量(并且 type(int) 是常量,不要弄错)等于表达式的情况 - 继承是类型 Type 的另一种“行为”。甚至不需要考虑继承,我的意思是,我们是否因为两个对象具有其他品质而拒绝比较它们?不,我们没有,因为平等总是被定义的。基本上,重点是不同类型之间没有重叠。

      正如我所说,只有一个原因,而且只有一个原因:懒惰。 :)

      【讨论】:

        猜你喜欢
        • 2013-04-11
        • 1970-01-01
        • 2012-02-14
        • 2014-02-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多