15

As in this example:

switch ( myObj.GetType ( ) )
{
    case typeof(MyObject):
        Console.WriteLine ( "MyObject is here" );
        break;
}
Joan Venge
  • 315,713
  • 212
  • 479
  • 689
  • Thanks, that's what I think too, but why? – Joan Venge Nov 10 '09 at 20:38
  • 12
    http://blogs.msdn.com/peterhal/archive/2005/07/05/435760.aspx – Brother Erryn Nov 10 '09 at 20:39
  • 2
    @RHSeeger: There was no statement in Petter Hallam's blog post that can be construed as "people are too stupid to understand the concept" nor that the language designers played it safe because there are stupid people in the world. – jason Nov 10 '09 at 20:57
  • 6
    It has nothing whatsoever to do with people "being too stupid". Peter's point was that people would find this behaviour *surprising*, not *incomprehensible*. C# has been carefully designed to be unsurprising whenever possible. – Eric Lippert Nov 10 '09 at 21:19
  • 3
    If you are ever tempted to write a `switch` statement that switches on the type of an object, you really need to refactor and delegate the cases to the object implementations. In a well-designed OO system there should *never* be a need to do this. – Daniel Pryden Nov 10 '09 at 21:19
  • In the absence of more elaborate pattern matching, it makes sense not to switch on arbitrary types (including the type System.Type.) I think it's a stretch to claim that the designers are "play[ing] it safe because there are stupid people in the world." – mqp Nov 10 '09 at 21:20
  • 1
    Be aware that switching on object type is a strong code smell. Switching on a type to figure out what code to execute/which method to call is like saying "I wish all my objects I will encounter here had a method I could call right now". So a better question might be "Why do I have to switch on object type?" and when you answer that question fix the problem instead of the symptom :) – Rune FS Nov 10 '09 at 21:32
  • @Daniel, mquander and Rune: I certainly agree. But what about cases where say you need to add a shared event handler to every Button in an app, etc at runtime? – Joan Venge Nov 10 '09 at 22:45
  • possible duplicate of [C# switch statement limitations - why?](http://stackoverflow.com/questions/44905/c-sharp-switch-statement-limitations-why) – outis Jan 26 '12 at 04:52
  • Possible duplicate of [Is there a better alternative than this to 'switch on type'?](https://stackoverflow.com/questions/298976/is-there-a-better-alternative-than-this-to-switch-on-type) – Michael Freidgeim Aug 30 '17 at 08:53

10 Answers10

20

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: ...
} 
Tim Ridgely
  • 2,400
  • 1
  • 18
  • 25
15

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.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Thanks Eric, a good answer. Someone here said vb.net can do this. How does it work? Is it a compromise on vb.net performance or just a different construct than the C# switch statement? – Joan Venge Nov 10 '09 at 22:38
  • 1
    The documentation for the VB construct is here: http://msdn.microsoft.com/en-us/library/cy37t14y.aspx – Eric Lippert Nov 10 '09 at 23:11
  • Thanks Eric, just assumed the poster here was correct. I understand it now. – Joan Venge Nov 10 '09 at 23:55
  • From the [link above](http://msdn.microsoft.com/en-us/library/cy37t14y.aspx), for *testexpression*, the documentation for the VB construct says: "Required. Expression. Must evaluate to one of the elementary data types (**Boolean**, **Byte**, **Char**, **Date**, **Double**, **Decimal**, **Integer**, **Long**, **Object**, **SByte**, **Short**, **Single**, **String**, **UInteger**, **ULong**, and **UShort**)." So, like C#, VB.NET does ***not allow anything*** as the test expression for a **Select...Case** statement. – DavidRR Mar 18 '14 at 12:43
  • "A given value usually has many types" - this doesn't make any sense at all. Every value in .NET only has **one** type. What that type inherits from, is a base class for, has as generic arguments, is completely irrelevant. – Mr. TA Sep 23 '15 at 16:57
13

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.

Community
  • 1
  • 1
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Thanks Marc. If they handled strings using a Dictionary, why didn't MS do the same for other types by using a Dictionary? – Joan Venge Nov 10 '09 at 20:49
  • 3
    vb.net allows anything in the switch however – Pondidum Nov 10 '09 at 21:23
  • 1
    @joan because it only works for compile time constants (such as string litterals). The key is used in the IL code and hence needs to be known at compile time – Rune FS Nov 10 '09 at 21:29
  • @Pondidum - I expect the VB.Net switch simply implements a series of If's in the IL. If you single-step through, do you see the code stop on each "case" instead of jumping to the correct one immediately? – Phil Whittington Jan 26 '15 at 10:22
  • @RuneFS the expressions in the form of `typeof(SomeType)` are constant, too, yet aren't allowed. Perhaps `typeof(T)` or `typeof(SomeType)` (generics) would still be prohibited because there is no way to check uniqueness at compile time, but short of that, any type should be legal. – Mr. TA Sep 23 '15 at 16:55
  • @Mr.TA they actually aren't compile time constants but are simply an expression where you can be pretty sure you know what value it will evaluate to. – Rune FS Sep 23 '15 at 18:16
  • @RuneFS they aren't compile-time constants but can be made to be (except for generics). – Mr. TA Oct 15 '15 at 15:36
  • @Mr.TA the questions is not what's possible. Because everything is possible. The question was why you couldn't use the typeof operator and the answer is because it's not a compile time constant. It's a shorthand for calling a method and that method relies on a parameter of type `RuntimeTypeHandle` and there's no compile time constants when it comes to calling a method with a runtime argument and typeof can't be made compile time constant because it wouldn't work with generics then and it would work with dynamics nor code created dynamically. All of which is part of C# – Rune FS Oct 15 '15 at 20:40
  • @RuneFS C# defines the `typeof` operator; how it's implemented doesn't matter. Ditto for the `switch` statement. W/o generics, `typeof` case blocks make sense from C#'s perspective. That's all that matters. The question was, why is it illegal in C#; the answer, there is no strong reason for it - other than it being a lower priority and availability of workaround, with switching on strings, `if` statements, etc. – Mr. TA Oct 16 '15 at 21:24
  • @Mr.TA GetType() is a method so you are clearly interpreting the question different than I, making this discussion pointless since we are comparing apples and biscuits – Rune FS Oct 17 '15 at 10:59
  • @RuneFS the expression in the `switch` itself can have a call to `GetType()` or any other method/property - that part is irrelevant. The question is about the `case` statement having the `typeof()` operator. There is a big difference between `typeof()` and `obj.GetType()`. – Mr. TA Oct 18 '15 at 01:30
  • @Mr.TA to you yes. To me no. Whether the method being called is named GetType or GetTypeFromHandle or whether it's static or an instance method makes litte difference to me. – Rune FS Oct 18 '15 at 19:07
  • @RuneFS there is a difference between the "language" (C#) and how it gets implemented (.NET IL). It's not whether it's static or instance method that gets written out for a specific language construct; rather, what gets written out is not relevant at all - neither to me nor to you. The question is about the language and when discussing it, we should stick to discussing the language constructs. – Mr. TA Oct 19 '15 at 16:17
7

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).

Kyle Trauberman
  • 25,414
  • 13
  • 85
  • 121
5

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."

Dan Diplo
  • 25,076
  • 4
  • 67
  • 89
  • Except uniqueness can be determined for `typeof()` expressions just fine (except for generics), then the order doesn't matter. – Mr. TA Sep 23 '15 at 16:59
2

It's that typeof is not a constant and cases must be constants.

LJM
  • 6,284
  • 7
  • 28
  • 30
2

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

nos
  • 223,662
  • 58
  • 417
  • 506
2

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;
}
Sergei Zinovyev
  • 1,238
  • 14
  • 14
1

Why don't you just tostring() it?

C Bauer
  • 5,003
  • 4
  • 33
  • 62
-1

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. :)

Mr. TA
  • 5,230
  • 1
  • 28
  • 35