Case Switching on CLR Types

As most .NET developers know, you cannot do case/switch on CLR types and one of the reasons for it was explained pretty well years ago by Peter Hallam on the C# team.

But there are many cases where you would like to iterate through a list of objects if mixed types and do specific things depending on it’s type. For fun I started to try out different ways to do it, some are better than others but they all do the same thing, more or less. I’m also exploring method extensions, method chaining and lambdas and some of the samples almost started to become fluent and DLS like.

Note

Oh, I’m as far from a C# language expert as anyone can be, so there are other ways of doing this I’m sure. The random code below is just me playing around for a simple way of doing case/switching on types that worked in the code I’m currently working on.

Also, if you would like a derived class to do something special, you normally override a method, send in some parameters and let that code whatever it so special. That’s basic OOD, see the Eat() method in the sample code below. But there are cases where you for one reason or other would not like to do this. Enough of that, this is just for fun.

A List of Pets

I was working with a class hierarchy of pets like this:

namespace TypeCase

{

    public abstract class Pet

    {

        public string Name { get; set; }

        public abstract void Eat();

 

        public override string ToString()

        {

            return Name;

        }

 

        public Pet Case<T>(Action<T> action) where T : Pet

        {

            if (this is T)

                action.Invoke((T)this);

 

            return this;

        }

    }

 

    public class Dog : Pet

    {

        public override void Eat()

        {

            Console.WriteLine(Name + " eats cats.");

        }

    }

 

    public class Cat : Pet

    {

        public override void Eat()

        {

            Console.WriteLine(Name + " eats mice.");

        }

    }

}

 

We got a Cat and a Dog which are both different types of Pet. They have a Name and they can Eat() which is good enough for testing.

Creating the List

I’m creating a simple typed List like this:

            var pets = new List<Pet>

                           {

                               new Cat { Name = "Morris"},

                               new Dog { Name = "Buster"}

                           };

Now we have something to play with. First do something you often see, especially in .NET 1.x code.

Case Switching on Strings

It’s perfectly fine to switch on strings, so this is quite common:

            foreach (var pet in pets)

            {

                switch (pet.GetType().ToString())

                {

                    case "TypeCase.Cat":

                        Console.WriteLine("A cat called " + pet);

                        break;

                    case "TypeCase.Dog":

                        Console.WriteLine("A dog called " + pet);

                        break;

                }

            }

I’m not too fond of this, because you may rename Cat or Dog in the future, or change namespace of “TypeCase” to something else, and even though renaming stuff with Resharper is powerful, strings are often missed. It would have been nice to:

                    case typeof(Cat):

But that’s not allowed. The case must be a constant.

If Is

A much safer way is to use if … else if … and instead of using string comparing, check the type with the is statement. It’s also faster to type:

            foreach (var pet in pets)

            {

                if (pet is Cat) Console.WriteLine("A cat called " + pet);

                else if (pet is Dog) Console.WriteLine("A dog called " + pet);

            }

This code is perfectly fine and I’ve used it many times. But what if I wanted to have a Case-like syntax?

Method Extension on Type

I’m thinking of a syntax like this one:

                pet.GetType().

                    Case(typeof(Cat), () => Console.WriteLine("A cat called " + pet)).

                    Case(typeof(Dog), () => Console.WriteLine("A dog called " + pet));

 

In this case we’re extending the Type type with a Case method, like this:

    public static class TypeExt

    {

        public static Type Case(this Type t, Type what, Action action)

        {

            if (t == what)

                action.Invoke();

 

            return t;

        }

    }

The Action parameter encapsulate the anonymous method we’re sending in, containing the stuff we want to do with the pet in question. In the Case() extension method we’re testing to see if we’re given the right Type and Invoke() the anonymous method if so.

Important Note: Without going into details, just make sure you don’t fall into a case of “Access to modified closure” when doing for(each) loops around anonymous methods. To be safe, you have to create a local pet-variable outside of the method:

            foreach (var pet in pets)

            {

                //some code

                var safePet = pet;

                pet.GetType().

                    Case(typeof(Cat), () => Console.WriteLine("A cat called " + safePet)).

                    Case(typeof(Dog), () => Console.WriteLine("A dog called " + safePet));

                //some more code

            }

 

Better Method Extension on Type

But I’m not happy with this syntax. If feels more cumbersome than the if…is…if…else… syntax, and whenever you see the use of typeof() in code like this, generics can do some work for you. So I’m going for a syntax like this:

                pet.GetType().

                    Case<Cat>(obj => Console.WriteLine("A cat called " + pet)).

                    Case<Dog>(obj => Console.WriteLine("A dog called " + pet));

This requires a new method extension:

    public static class TypeExt

    {

        public static Type Case<T>(this Type t, Action<Type> action)

        {

            if (t == typeof(T))

                action.Invoke(t);

 

            return t;

        }

    }

Looks better, but you still risk getting into the issues with modified closure above and I would like to work on the “obj” parameter as if it was the Pet objekt itself, not the Type. Let’s make it even better:

Even Better Method Extension on Pet

Now I’m going for a syntax that looks like this:

            foreach (var pet in pets)

            {

                pet.

                    Case<Cat>(c => Console.WriteLine("A cat called " + c)).

                    Case<Dog>(d => Console.WriteLine("A dog called " + d));

 

                pet.Eat();

            }

As you can see, the syntax is cleaner and I can work with the pet object itself as a parameter handled to anonymous method in the lambda statement.

To do this I have to create a method extension which knows about the Pet class:

    public static class PetExt

    {

        public static Pet Case<T>(this Pet pet, Action<T> action) where T : Pet

        {

            if (pet is T)

                action.Invoke((T)pet);

 

            return pet;

        }

    }

It’s not a generic Case Switcher on Type, but it feels good to me and is easy to work with. And you don’t have the issue with access to modified closures with this one.

Refined Method Extension on List of Pets

I’m throwing in a final variant here, adding the Case method to the list itself:

            pets.

                Case((Cat c) =>

                        {

                            Console.WriteLine("A cat called " + c);

                            c.Eat();

                        }).

                Case<Dog>(d =>

                        {

                            Console.WriteLine("A dog called " + d);

                            d.Eat();

                        });

 

            pets.

                Case((Cat c) => Console.WriteLine("A cat called " + c)).

                Case<Dog>(d => Console.WriteLine("A dog called " + d));

 

As you can see, there are two ways syntactically to provide the type and the simple extension method for this variant looks like this:

    public static class PetListExt

    {

        public static List<Pet> Case<T>(this List<Pet> pets, Action<T> action) where T : Pet

        {

            foreach (var pet in pets)

            {

                if (pet is T)

                    action.Invoke((T)pet);

            }

 

            return pets;

        }

    }

 

That’s it. I’ve seen a number of more complex ways to do roughly the same, but I’m not trying to create the ultimate Switch/Case framework, just playing around with c# to create something simple that may make the code easier to read (and fun to code).

1 Comment

  • Can you give more examples of situations where you want to do this? This one was trivial, and I don't see it extrapolated to real situations too easily.

    For this simple example, we can simply use reflection to check and print the class name of the type.

    One other thing you can consider doing is simply casting it to the type you want, and then catching the casting exception. This is much faster than using reflection.

    However, I do appreciate the very robust lambda expressions you have here, and can see their power. Thanks for that!

Comments have been disabled for this content.