Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to conditionally cast to multiple types in c#

I'm looking at a function with this pattern:

if( obj is SpecificClass1 )
{
   ((SpecificClass1)obj).SomeMethod1();
}
else if( obj is SpecificClass2 )
{
   ((SpecificClass2)obj).SomeMethod2();
}
else if( obj is SpecificClass3 )
{
   ((SpecificClass3)obj).SomeMethod3();
}

and get a code analysis warning: CA1800 Do not cast unnecessarily.

What's a good code pattern I can use to replace this code with that will be performant and concise.

Update

I didn't say, but obj is declared with type object.

I originally asked two questions here. I've split one off (which nobody had yet answered anyway): Why wouldn't the compiler optimize these two casts into one?

like image 826
Scott Langham Avatar asked Feb 24 '14 11:02

Scott Langham


2 Answers

Interface

The best way would be to introduce an interface that all the types implement. This is only possible if the signatures match (or you don't have too many differences).

Using as

If creating an interface is not an option, you can get rid of the CA message by using the following pattern (though this also introduces unnecessary casts and therefore degrades performance a bit):

var specClass1 = obj as SpecificClass1;
var specClass2 = obj as SpecificClass2;
var specClass3 = obj as SpecificClass3;
if(specClass1 != null)
   specClass1.SomeMethod1();
else if(specClass2 != null)
   specClass2.SomeMethod2();
else if(specClass3 != null)
   specClass3.SomeMethod3();

You can also change it to this structure (from my point of view, the above is better in terms of readability):

var specClass1 = obj as SpecificClass1;
if (specClass1 != null)
   specClass1.SomeMethod1();
else
{
    var specClass2 = obj as SpecificClass2;
    if (specClass2 != null)
        specClass2.SomeMethod2();
    else
    {
        var specClass3 = obj as SpecificClass3;
        if (specClass3 != null)
            specClass3.SomeMethod3();
    }
}

Registering the types in a dictionary

Also, if you have many types that you want to check for, you can register them in a dictionary and check against the entries of the dictionary:

var methodRegistrations = new Dictionary<Type, Action<object> act>();
methodRegistrations.Add(typeof(SpecificClass1), x => ((SpecificClass1)x).SomeMethod1());
methodRegistrations.Add(typeof(SpecificClass2), x => ((SpecificClass2)x).SomeMethod2());
methodRegistrations.Add(typeof(SpecificClass3), x => ((SpecificClass3)x).SomeMethod3());

var registrationKey = (from x in methodRegistrations.Keys 
                       where x.IsAssignableFrom(obj.GetType()).FirstOrDefault();
if (registrationKey != null)
{
    var act = methodRegistrations[registrationKey];
    act(obj);
}

Please note that the registrations are easily extendable and that you can also call methods with different arguments in the action.

like image 170
Markus Avatar answered Nov 15 '22 07:11

Markus


To avoid the double casting you could do the following

var objClass1= obj as SpecificClass1;
if(objClass1!=null)
   objClass1.SomeMethod1();

Regarding the pattern you could make all these classes implement a common interface and make your method receive the interface.

public void SomeMethod(ISpecificInterface specific)
{
  specific.SomeMethod1();
}
like image 44
Luis Filipe Avatar answered Nov 15 '22 09:11

Luis Filipe