Simon Linder
Simon Linder

Reputation: 3428

Casting object to specified class best practice

I have a method that gets an object that can be of various types. Depending of the type of the object I need to do different actions. My current code is like this:

public void SomeMethod(object obj)
{
    int? someId = null;

    Class1 class1 = obj as Class1;
    Class2 class2 = obj as Class2;
    Class3 class3 = obj as Class3;
    Class4 class4 = obj as Class4;

    if (class1 != null && class1.SomeProperty != null)
    {
        someId = class1.SomeProperty.Id;
    }
    else if (class2 != null && class2.AnotherProperty != null)
    {
        someId = class2.AnotherProperty.AnotherId;
    }
    ...

    AnotherMethod(someId);
}

I am not quite satisfied with this code as I do have some unnecessary casts. Which would be the most efficient way to do such an operation? I was thinking about something like:

if (obj.GetType().Equals(typeOf(Class1))
{
    someId = ((Class1)obj).SomeProperty.Id;
}

Any suggestions?

Upvotes: 1

Views: 142

Answers (6)

HRold
HRold

Reputation: 229

I would use:

if (obj is Class1 && ((Class1)obj).SomeProperty)
    someId = ((Class1)obj).SomeProperty.Id;

Final code would look like this:

if (obj is Class1 && ((Class1)obj).SomeProperty)
    someId = ((Class1)obj).SomeProperty.Id;
else if (obj is Class2 && ((Class2)obj).AnotherProperty)
    someId = ((Class2)obj).AnotherProperty.Id;
else if (obj is Class3 && ((Class3)obj).OnceAgainAnotherProperty)
    ...

In every if and else if conditions, if the first check obj is X fails it will not do the second ((X)obj).XProperty. So at the end of the day, only two casts will be performed, even if your object as the type Class4.

Upvotes: 0

Mehmet Ataş
Mehmet Ataş

Reputation: 11549

I think you should first try doing this without casting. You can define an interface and explicitly implement this interface for each Classn. I assume Classn classes are not third party and you can modify them.

public interface IIdProvider {
    int? Id { get; }
}

public class Class1 : IIdProvider {
    int? IIdProvider.Id { 
        get {
            return SomeProperty != null ? SomeProperty.Id : null;
        }
    }
}

public class Class2 : IIdProvider {
   int? IIdProvider.Id { 
        get {
            return AnotherProperty != null ? AnotherProperty.AnotherId : null;
        }
    }
}

Having this, SomeMethod does not care where the Id comes from. If you want to add Class5, Class6 etc you do not have to modify SomeMethod. Instead each newly added class will take care of the Id thing itself. Having this, we can do the following.

public void SomeMethod(IIdProvider obj)
{
    AnotherMethod(obj.Id);
}

Upvotes: 2

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726579

Both alternatives are pretty bad, because they do explicit casts, and dispatch by checking the type manually. This is error-prone, and leads to inevitable maintenance headaches when you expand the list of types that your method needs to handle.

Starting with .NET 4.0, you have a better option - dispatch using dynamic:

public void SomeMethod(dynamic obj) {
    SomeMethodImpl(obj);
}
private void SomeMethodImpl(Class1 obj) {
    // Perform actions specific to Class 1
}
private void SomeMethodImpl(Class2 obj) {
    // Perform actions specific to Class 2
}
private void SomeMethodImpl(Class3 obj) {
    // Perform actions specific to Class 3
}
private void SomeMethodImpl(Class4 obj) {
    // Perform actions specific to Class 4
}
private void SomeMethodImpl(object obj) {
    // Catch all
}

Now you can place the code specific to a class in its own overload of SomeMethodImpl, without running a chain of conditional statements. Expanding your method to handle additional types is simple as well - all you need to do is adding another overload.

Note: I assume that the signature of SomeMethod must remain the same - in other words, you cannot address this problem simply by overloading SomeMethod.

Upvotes: 5

Carbine
Carbine

Reputation: 7903

Polymorphism??

public void SomeMethod(Class1 class1)
{
    AnotherMethod(class1.SomeProperty.Id);
}

public void SomeMethod(Class2 class2)
{
    AnotherMethod(class2.NewProperty.Id);
}
.
.
.

Instead why not call AnotherMethod(class1.SomeProperty.Id);,.. AnotherMethod(class2.NewProperty.Id) directly if that is all you wanted to do...

If you really want to check if the object is of certain type use if(obj is Class1) instead of casting it

Upvotes: 2

Lanorkin
Lanorkin

Reputation: 7504

In this particular case (get single value of a variable), it could be more efficient to use switch-like approach, something like:

public int? GetSomeId(object obj) {
    Class1 class1 = obj as Class1;
    if (class1 != null)
    {
        return class1.SomeProperty != null ? class1.SomeProperty.Id : (int?)null;
    }

    Class2 class2 = obj as Class2;
    if (class2 != null)
    {
        return class2.AnotherProperty != null ? class2.AnotherProperty.AnotherId : (int?)null;
    }

    ....

    return null;
}

Upvotes: 1

cackharot
cackharot

Reputation: 698

as keyword exists for programmer conciseness. It gets translated to

Class1 class1 = obj is Class1 ? (Class1)obj : null;

I thinks go with as as it looks more readable and also does not affect performance.

Upvotes: 0

Related Questions