Search code examples
c#castingdowncastupcasting

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


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.


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