Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

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 

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

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.

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

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?

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

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

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

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.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top