Can an upcasted object be downcasted again without trying a cast for every derived class type of the base class type?

StackOverflow https://stackoverflow.com/questions/12990327

Question

I have case where am given a collection of objects that all derive from the same base class. If I iterate over the collection and check each item's type, I can see that the object is of a derived type and then handle it accordingly. What I would like to know is if there is an easier way of performing the check for the derived type besides what I am already doing. Code repetition typically isn't required, so my current methodology seems a bit off to me.

class A {}
class B : A {}
class C : A {}
class D : C {}

class Foo
{
    public List<A> Collection { get; set; }
}

class Bar
{
    void Iterate()
    {
        Foo f = new Foo();
        foreach(A item in f.Collection)
        {
            DoSomething(a);
        }
    }

    void DoSomething(A a)
    {
        ...

        B b = a as B;
        if(b != null)
        {
            DoSomething(b);
            return;
        }

        C c = a as C;
        if(c != null)
        {
            DoSomething(c);
            return;
        }

        D d = a as D;
        if(d != null)
        {
            DoSomething(d);
            return;
        }
    };

    void DoSomething(B a){};
    void DoSomething(C a){};
    void DoSomething(D a){};
}

I am working with a web service where every web service must have the same result type.

class WebServiceResult
{
    public bool Success { get; set; }
    public List<Message> Messages { get; set; }
}

class Message
{
    public MessageType Severity { get; set; } // Info, Warning, Error
    public string Value { get; set; } //
}

class InvalidAuthorization: Message
{
    // Severity = MessageType.Error
    // Value = "Incorrect username." or "Incorrect password", etc.
}

class InvalidParameter: Message
{
    // ...
}

class ParameterRequired: InvalidParameter
{
    // Severity = MessageType.Error
    // Value = "Parameter required.", etc.
    public string ParameterName { get; set; } //
}

class CreatePerson: Message
{
    // Severity = MessageType.Info
    // Value = null
    public int PersonIdentifier { get; set; } // The id of the newly created person
}

The goal is that we can return as many different types of messages back to the client as we want. Instead of getting a single message per web service call, the callee can know about all of their mistakes/successes in a single trip and to eliminate string parsing specific information from the message.

I originally though about using generics, but since the web service could have varying message types, the collection was broadened to use the base message class.

Was it helpful?

Solution

It may be possible to move DoSomething to A and have each subclass provide their own implementation:

public abstract class A
{
    abstract void DoSomething();
}

void Iterate()
{
    Foo f = new Foo();
    foreach(A item in f.Collection)
    {
        item.DoSomething();
    }
}

OTHER TIPS

An idea would be to use a generic constraint on your base class or an interface.

public class MyClass<T> where T : BaseClass, IInterface
{
   public void executeCode<T>(T param) {};
}

So MyClass<T> takes only a certain type, executeCode will have an idea what methods are exposed and what operations can be performed on the data of the object passed. This avoids the need to cast because you are specifying a contract that must be followed.

typeof(ParentClass).IsAssignableFrom(typeof(ChildClass));

Return true is the cast is possible.

Also possible this way:

typeof(ParentClass).IsAssignableFrom(myObject.GetType());

But in your example, you actually call a methods for each object type. So you would need the cast anyway, unless you don't mind refactoring to not have a collection of overload.

Something like this if you want to keep the overload:

foreach(A item in f.Collection)
{
    Type itemType = item.GetType();

    if (typeof(B).IsAssignableFrom(itemType)
        DoSomethingB(item);
    else if (typeof(C).IsAssignableFrom(itemType)
        DoSomethingC(item);
    //...
}

EDIT: I like more Lee's answer. Adding virtual/override function to the class type would be a better design and way easier to handle, unless the DoSomething really has nothing to do being in the classes.

Lee is right. Just let the item decide, what to do. It knows it's type best and therefore knows what to do. You might even give some standard implementation, if it is the same as in A, by not making it abstract, but virtual. Be aware though, that the compiler wont ask for an implementation then.

public class A 
{
  public virtual DoSomething(){"What A needs doning!"}
}

public class B : A
{
  public override DoSomething() {"What B needs doing!"}
}

Another way would be the use of Interfaces.

public interface IAinterface 
{
  void DoSomething();
}

public class A : IAinterface
{
  void DoSomething(){...}
}

public class B : IAinterface
{
  void DoSomething(){...}
}

This would be more like Lees suggestion, although interfaces and abstract base classes work a bit different in the background.

I usually prefer the upper one, because I usually tend to give the base class some standard behavior and only implement derived classes, when there is something different.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top