简体   繁体   中英

Why doesn't C# switch statement allow using typeof/GetType()?

As in this example:

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

Second on Peter Hallam's post; it's a great explanation.

You can use TypeCode to work with simple types, though.

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

I would add to Peter's excellent analysis the following thought:

Fundamenatally, the purpose of a "switch" is to choose one of some number of distinct possibilities . A given value of enum, integer, Boolean or string type can only be one value, so it makes sense to "switch" on such a value. But types are fundamentally different. A given value usually has many types. Types frequently overlap . The proposed "type switch" does not match the stated purpose of the switch construct.

The problem is that switch (per the spec) only works with primitives (int etc) and strings. But yes, it would be nice to have F#-style matching .

From §8.7.2:

 switch-label: case constant-expression : default : 

... The governing type of a switch statement is established by the switch expression. If the type of the switch expression is sbyte, byte, short, ushort, int, uint, long, ulong, char, string, or an enum-type, then that is the governing type of the switch statement. Otherwise, exactly one user-defined implicit conversion (§6.4) must exist from the type of the switch expression to one of the following possible governing types: sbyte, byte, short, ushort, int, uint, long, ulong, char, string. If no such implicit conversion exists, or if more than one such implicit conversion exists, a compile-time error occurs.

It is obvious, however, that working with such a restricted set allows for simple (and efficient) IL. Note that string is handled via a dictionary map to an integer.

There's a good blog post on MSDN by Peter Hallam which explains the problems of switching on non-constant values.

"The order of the case labels becomes significant in determining which block of code gets executed. Because the case label expressions are not constant the compiler cannot verify that the values of the case labels are distinct, so this is a possibility which must be catered to. This runs counter to most programmers' intuition about the switch statement in a couple of ways. Most programmers would be surprised to learn that changing the order of their case blocks changed the meaning of their program. To turn it around, it would be surprising if the expression being switched on was equal to an expression in a case label, but control didn't go to that label."

You could do

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

This works because switching only works on primitive types (as others have said).

就是typeof不是常数,而且情况必须是常数。

a switch in C# only works for integrals or strings. myObj.GetType() returns a Type, which is neither an integral or a string.

你为什么不只是tostring()呢?

In C# 7.0 you can do it. See 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;
}

There's no good reason for MS not to implement switching on types, other than laziness.

String switching is accomplished using "if(..Equals(..))"s with few cases and a Dictionary with many cases. Both of those approaches are defined for all .NET types, because System.Object has Equals and GetHashCode that are virtual.

One could say that, "switch can use expression of any type where Equals and GetHashCode are overridden", which automatically qualifies string, Type, etc. Yes, bad Equals/GetHashCode implementation will break the switch statement, but hey, you can also break the "==" operator, the "foreach" loop, and a bunch of other stuff, so I don't really see the "big problem" with switch being broken by programmer's mistake. But even if they don't want to allow it for all types, for whatever reason, certainly Type is safe, because Type.Equals() is well-defined and GetHashCode is also implemented.

Also, I don't buy the argument that you have consider inheritance; switch goes to the case whose constant (and type(int) is a constant, make no mistake about that) is equal to the expression - inheritance is another "behavior" of the type Type. One doesn't even need to consider inheritance, I mean, do we refuse to compare 2 objects just because they have other qualities? No, we don't, because equality is always defined. Basically, point is, there's no overlapping between different types.

So as I said, there's one reason and one reason only: laziness. :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM