是否可以再次下载一个上升的对象而不为基类类型的每个派生类类型尝试强制转换?

时间:2012-10-20 16:18:18

标签: c# casting downcast upcasting

我有一个案例,给出了一组对象,这些对象都来自同一个基类。如果我遍历集合并检查每个项目的类型,我可以看到该对象是派​​生类型,然后相应地处理它。我想知道的是,除了我已经在做的事情之外,还有一种更简单的方法来执行派生类型的检查。代码重复通常不是必需的,所以我当前的方法对我来说似乎有点不对。

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

我正在使用Web服务,其中每个Web服务必须具有相同的结果类型。

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
}

目标是我们可以根据需要将尽可能多的不同类型的消息返回给客户端。每次Web服务调用都不会获得单个消息,被调用者可以在一次旅行中了解他们的所有错误/成功,并从消息中消除字符串解析特定信息。

我最初使用泛型,但由于Web服务可能有不同的消息类型,因此扩展了集合以使用基本消息类。

4 个答案:

答案 0 :(得分:4)

可以将DoSomething移动到A并让每个子类提供自己的实现:

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

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

答案 1 :(得分:1)

一个想法是在基类或接口上使用泛型约束。

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

因此MyClass<T>仅采用某种类型,executeCode将了解所公开的方法以及可以对传递的对象的数据执行的操作。 这样可以避免需要进行强制转换,因为您指定的是必须遵循的合同。

答案 2 :(得分:0)

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

返回true表示可以进行演员表。

也可以这样:

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

但是在您的示例中,您实际上为每个对象类型调用了一个方法。所以你无论如何都需要演员,除非你不介意重构没有过载的集合。

如果你想保持过载,可以这样:

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);
    //...
}

编辑:我更喜欢Lee的回答。向类类型添加虚拟/覆盖功能将是一种更好的设计,并且更容易处理,除非DoSomething在类中没有任何关系。

答案 3 :(得分:0)

李是对的。让项目决定,做什么。它知道它的类型最好,因此知道该怎么做。您甚至可以给出一些标准实现,如果它与A中的相同,不是将其抽象化,而是虚拟实现。但请注意,编译器不会要求实现。

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

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

另一种方法是使用Interfaces。

public interface IAinterface 
{
  void DoSomething();
}

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

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

这更像是Lees的建议,尽管接口和抽象基类在后台工作有点不同。

我通常更喜欢上面的一个,因为我通常倾向于给基类一些标准行为,并且当有不同的东西时只实现派生类。