using the 'is' keyword in a switch in c#
Asked Answered
G

12

30

I'm currently adding some new extended classes to this code:

foreach (BaseType b in CollectionOfExtendedTypes) {
  if (b is ExtendedType1) {
    ((ExtendedType1) b).foo = this;

  }
  else if (b is ExtendedType2) {
    ((ExtenedType2) b).foo = this;

  } 
  else {
    b.foo = this;

  }
}

and was curious if there is a way to use the is keyword functionality in a switch statement?

Gies answered 21/10, 2008 at 21:52 Comment(1)
Unless I'm mistaken, you should be able to change class design so that if-else construction will become unnecessary in favour of simple "b.foo = this;"Semblance
M
16

This really looks like a situation for a good polymorphic implementation. If you override the appropriate methods in the derived classes, you may not need the checks in the loop at all.

Marela answered 21/10, 2008 at 22:6 Comment(3)
Could you give an example please?Sip
@Sip sourcemaking.com/refactoring/…Marela
There are certainly times when you can't so this howeverEquuleus
S
45

The latest version of C# (7) now includes this functionality

Type pattern

The type pattern enables concise type evaluation and conversion. When used with the switch statement to perform pattern matching, it tests whether an expression can be converted to a specified type and, if it can be, casts it to a variable of that type. Its syntax is:

   case type varname 
Sun answered 30/11, 2017 at 14:6 Comment(1)
This is fantastic! Thank you for adding this to an old question, this should now be marked as the answer.Hatter
M
16

This really looks like a situation for a good polymorphic implementation. If you override the appropriate methods in the derived classes, you may not need the checks in the loop at all.

Marela answered 21/10, 2008 at 22:6 Comment(3)
Could you give an example please?Sip
@Sip sourcemaking.com/refactoring/…Marela
There are certainly times when you can't so this howeverEquuleus
H
5

Nope. See

C# switch statement limitations - why?

Hydrolyze answered 21/10, 2008 at 21:54 Comment(0)
T
4

In C# it's not possible to use the "is" keyword as part of a switch statement. All case labels in a switch must evaluate to constant expressions. "is" is not convertible to a constant expression.

I definately feel the pain though when it comes to switching on types. Because really the solution you outlined works but it's a conveluted way of saying for x do y, and a do b. It would be much more natular to write it more like the following


TypeSwitch.Do(
    sender,
    TypeSwitch.Case<Button>(() => textBox1.Text = "Hit a Button"),
    TypeSwitch.Case<CheckBox>(x => textBox1.Text = "Checkbox is " + x.Checked),
    TypeSwitch.Default(() => textBox1.Text = "Not sure what is hovered over"));

Here's a blog post I wrote on how to achieve this functionality.

http://blogs.msdn.com/jaredpar/archive/2008/05/16/switching-on-types.aspx

Tarantass answered 21/10, 2008 at 21:57 Comment(0)
T
4

As mentioned in the answer from MikeT, you are able to use pattern mathing which requires C# 7.

Here's an example for your code:

foreach (BaseType b in CollectionOfExtendedTypes) {
  switch (b) {
    case ExtendedType1 et1:
        // Do stuff with et1.
        et1.DoStuff();
        break;
    case ExtendedType2 et2:
        // Do stuff with et2.
        et2.DoOtherStuff();
        break;
    default:
        // Do something else...
        break;
  }
}
Turnery answered 6/4, 2018 at 8:11 Comment(0)
V
2

While it is not possible to use switch statement for checking types, it is not impossible to reduce the problem to a more manageable codebase.

Depending on the specific situation and requirement I would consider.

  • Using a IDictionary<Type, T> to store the result in a dictionary. T could itself be a delegate that you can call on. This will work if you don't need to worry about inheritance - catering for inheritance will take a little more work.

  • Using the type name of the class (which is string) inside the switch statement. This uses switch (b.GetType().Name) and there is no option for deep inheritance structure.

Vedetta answered 21/10, 2008 at 22:4 Comment(0)
K
1

You could add a method getType() to BaseType that is implemented by each concrete subclass to return a unique integral ID (possibly an enum) and switch on that, yes?

Kristalkristan answered 21/10, 2008 at 21:59 Comment(1)
this seems natural to me as well but they are not my base classes to play around with. thx thoughGies
B
1

Not really, switches match a variable (string or int (or enum) ) with a constant expression as the switch statement.

http://msdn.microsoft.com/en-us/library/06tc147t(VS.71).aspx

Bedcover answered 21/10, 2008 at 22:1 Comment(0)
O
1

You can use switch expressions and pattern matching in C# 8.0.

This is the documentation from Microsoft: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns

This blog post by Thomas Claudius shows how it is done. https://www.thomasclaudiushuber.com/2021/02/25/c-9-0-pattern-matching-in-switch-expressions/

Below the example from the url:

object obj = ...
string favoriteTask = obj switch
{
  Developer dev when dev.YearOfBirth == 1980 => $"{dev.FirstName} listens to metal",
  Developer dev => $"{dev.FirstName} writes code",
  Manager _ => "Create meetings",
  _ => "Do what objects do",
};
Obsequious answered 4/4, 2023 at 16:31 Comment(1)
I like to dig down until I find these treasure-like answers. Thanks, man!Bolling
S
0

Type-cases and object oriented code don't seem to get on that well together in my experience. The approach I prefer in this situation is the double dispatch pattern. In short:

  • Create a listener type with an empty virtual method Process(ExtendedTypeN arg) for each extended type you will be dispatching over.
  • Add a virtual method Dispatch(Listener listener) to the base type that takes a listener as argument. Its implementation will be to call listener.Process((Base) this).
  • Override the Dispatch method in each extended type to call the appropriate overload of Process in the listener type.
  • Extend the listener type by overriding the appropriate Process method for each subtype you are interested in.

The argument shuffling dance eliminates the narrowing cast by folding it into the call to Dispatch -- the receiver knows its exact type, and communicates it by calling back the exact overload of Process for its type. This is also a big performance win in implementations such as .NET Compact Framework, in which narrowing casts are extremely slow but virtual dispatch is fast.

The result will be something like this:


public class Listener
{
    public virtual void Process(Base obj) { }
    public virtual void Process(Derived obj) { }
    public virtual void Process(OtherDerived obj) { }
}

public class Base
{
    public virtual void Dispatch(Listener l) { l.Process(this); }
}

public class Derived
{
    public override void Dispatch(Listener l) { l.Process(this); }
}

public class OtherDerived
{
    public override void Dispatch(Listener l) { l.Process(this); }
}

public class ExampleListener
{
    public override void Process(Derived obj)
    {
        Console.WriteLine("I got a Derived");
    }

    public override void Process(OtherDerived obj)
    {
        Console.WriteLine("I got an OtherDerived");
    }

    public void ProcessCollection(IEnumerable collection)
    {
        foreach (Base obj in collection) obj.Dispatch(this);
    }
}
Septillion answered 16/1, 2009 at 4:23 Comment(0)
S
-1

There's another thing to think about besides the way that the compiler handles switch statements, and that's the functioning of the is operator. There's a big difference between:

if (obj is Foo)

and

if (obj.GetType() == typeof(Foo))

Despite the name, the is operator tells you if an object is compatible with a given type, not if it is of the given type. This leads to not-entirely-obvious bugs (though this one's pretty obvious) that look like:

if (obj is System.Object)
{
   //this will always execute
}
else if (obj is Foo)
{
   //this will never execute
}

Many of the suggestions here point you in the direction of using the object's type. That's fine if what you really want is logic associated with each type. But if that's the case, walk carefully when using the is operator.

Also: though you can't modify these base types, that doesn't mean that you can't use Owen's suggestion. You could implement extension methods:

public enum MyType { Foo, Bar, Baz };
public static class MyTypeExtension
{
   public static MyType GetMyType(this Foo o)
   {
      return MyType.Foo;
   }
   public static MyType GetMyType(this Bar o)
   {
      return MyType.Bar;
   }
   public static MyType GetMyType(this Baz o)
   {
      return MyType.Baz;
   }
}

Then you can use a switch statement:

switch (myObject.GetType())
{
   case MyType.Foo:
     // etc.
Stereophonic answered 21/10, 2008 at 22:56 Comment(1)
Won't work for Foo obj = new Baz(); then obj.GetMyType() returns Foo and not Baz.Calcar
M
-2

In C#, I believe the switch statement only works with integers and strings.

Mintamintage answered 21/10, 2008 at 21:53 Comment(1)
it works with constants, so anything that can be used as a constant string, int, double, float, bool, etcSun

© 2022 - 2024 — McMap. All rights reserved.