15

As in this example:

switch ( myObj.GetType ( ) )
{
    case typeof(MyObject):
        Console.WriteLine ( "MyObject is here" );
        break;
}
10
  • 12
    blogs.msdn.com/peterhal/archive/2005/07/05/435760.aspx Commented Nov 10, 2009 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. Commented Nov 10, 2009 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. Commented Nov 10, 2009 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. Commented Nov 10, 2009 at 21:19
  • 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 :) Commented Nov 10, 2009 at 21:32

10 Answers 10

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: ...
} 
Sign up to request clarification or add additional context in comments.

1 Comment

Any idea how to test for Emums?
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.

5 Comments

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?
The documentation for the VB construct is here: msdn.microsoft.com/en-us/library/cy37t14y.aspx
Thanks Eric, just assumed the poster here was correct. I understand it now.
From the link above, 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.
"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.
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.

13 Comments

Thanks Marc. If they handled strings using a Dictionary, why didn't MS do the same for other types by using a Dictionary?
vb.net allows anything in the switch however
@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
@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?
@RuneFS the expressions in the form of typeof(SomeType) are constant, too, yet aren't allowed. Perhaps typeof(T) or typeof(SomeType<T>) (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.
|
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).

2 Comments

How can this work, because the value is still not known at compile time!
@Philip: The case values are known at compile time, because they're hardcoded as strings in this case. It's not an optimal solution, but it does work.
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."

1 Comment

Except uniqueness can be determined for typeof() expressions just fine (except for generics), then the order doesn't matter.
2

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

Comments

2

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

Comments

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;
}

1 Comment

This is the correct answer for modern C#. Virtually all supported .NET implementations support C# 7.
1

Why don't you just tostring() it?

1 Comment

@PhilipWallace what does Marc's answer have to do with the ToString() approach?
-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. :)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.