为什么';tc#switch语句是否允许使用typeof/GetType()?

为什么';tc#switch语句是否允许使用typeof/GetType()?,c#,.net,reflection,switch-statement,C#,.net,Reflection,Switch Statement,如本例所示: switch ( myObj.GetType ( ) ) { case typeof(MyObject): Console.WriteLine ( "MyObject is here" ); break; } 这是因为typeof不是常数,case必须是常数。C#中的开关仅适用于整数或字符串。myObj.GetType()返回一个类型,该类型既不是整数也不是字符串。有一个例子解释了打开非常量值的问题 “案例标签的顺序变为 在确定哪个块时有重

如本例所示:

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

这是因为typeof不是常数,case必须是常数。

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

有一个例子解释了打开非常量值的问题

“案例标签的顺序变为 在确定哪个块时有重要意义 因为 不支持大小写标签表达式 编译器无法验证的常量 案例标签的值是 不同,所以这是一种可能性 这是必须满足的,这是 与大多数程序员的直觉相反 关于一对夫妇中的switch语句 当然。大多数程序员都是这样 惊讶地发现改变 他们案件的顺序改变了案件的审理顺序 他们计划的意义。把它转过来 如果 打开的表达式是相等的 到大小写标签中的表达式,但 控件未转到该标签。“


问题是
开关
(根据规范)仅适用于原语(int等)和字符串。但是,是的,那太好了

根据§8.7.2:

。。。 switch语句的控制类型由switch表达式建立。 如果开关表达式的类型为sbyte、byte、short、ushort、int、uint、long, ulong、char、string或枚举类型,则这是开关的管理类型 陈述否则,只能存在一个用户定义的隐式转换(§6.4) 将开关表达式的类型转换为以下可能的控制类型之一: sbyte、byte、short、ushort、int、uint、long、ulong、char、string。如果没有这样的暗示 存在转换,或者如果存在多个此类隐式转换,则为编译时 发生错误


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

为什么不直接使用string()来处理它呢?

Peter Hallam文章的第二部分;这是一个很好的解释

不过,您可以使用类型代码处理简单类型

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

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

这是因为切换只在基本类型上起作用(正如其他人所说)。

我想在Peter出色的分析中补充以下思想:


基本上,“切换”的目的是从一些不同的可能性中选择一种。enum、integer、Boolean或string类型的给定值只能是一个值,因此“切换”这样的值是有意义的。但类型是根本不同的。给定的值通常有多种类型。类型经常重叠。建议的“类型开关”与开关构造的规定目的不匹配。

除了惰性之外,MS没有充分的理由不实施开关类型

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

可以说,“switch可以使用覆盖了Equals和GetHashCode的任何类型的表达式”,这会自动限定字符串、类型等。是的,坏的Equals/GetHashCode实现会破坏switch语句,但是嘿,你也可以破坏“==”操作符、“foreach”循环和其他一些东西,所以,我并不认为交换机被程序员的错误破坏有什么“大问题”。但即使他们不想允许所有类型都使用它,不管出于什么原因,类型肯定是安全的,因为Type.Equals()是定义良好的,并且还实现了GetHashCode

我也不赞成你考虑继承的论点;开关转到常量(类型(int)是常量,请不要搞错)等于表达式的情况-继承是类型的另一个“行为”。一个人甚至不需要考虑继承,我是说,我们拒绝比较2个对象仅仅是因为它们有其他的特性吗?不,我们没有,因为平等总是有定义的。基本上,重点是,不同类型之间没有重叠

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

在C#7.0中,你可以做到。看见


@RHSeeger:在Petter Hallam的博客文章中没有一句话可以解释为“人们太愚蠢而无法理解这个概念”,也没有说语言设计师出于安全考虑,因为世界上有愚蠢的人。这与人们“太愚蠢”无关。彼得的观点是,人们会发现这种行为令人惊讶,而不是无法理解。C#经过精心设计,在任何可能的情况下都不会让人感到意外。如果您试图编写一个切换对象类型的
switch
语句,那么您确实需要重构并将案例委托给对象实现。在一个设计良好的OO系统中,永远不需要这样做。请注意,打开对象类型是一种强烈的代码气味。打开类型以确定要执行的代码/要调用的方法就像说“我希望我在这里遇到的所有对象都有一个我现在可以调用的方法”。所以一个更好的问题可能是“为什么我必须打开对象类型?”当你回答这个问题时,解决问题而不是症状:)谢谢Marc。如果他们使用字典处理字符串,为什么MS不使用字典处理其他类型的字符串?vb.net允许开关中的任何内容however@joan因为它只适用于编译时常量(例如字符串literals)。密钥在IL代码中使用,因此需要在编译时知道time@Pondidum-我希望VB.Net开关只是在IL中实现一系列If。如果您单步执行,您是否看到代码在每个“案例”上都停止,而不是跳转到正确的案例
switch ( myObj.GetType().Name )
{
    case "MyObject":
        Console.WriteLine ( "MyObject is here" );
        break;
}
// ----- 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;
}