3

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

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

4 回答 4

4

可以移动DoSomethingA并让每个子类提供自己的实现:

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

void Iterate()
{
    Foo f = new Foo();
    foreach(A item in f.Collection)
    {
        item.DoSomething();
    }
}
于 2012-10-20T16:28:17.300 回答
1

一个想法是在您的基类或接口上使用通用约束。

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

所以MyClass<T>只取某种类型,executeCode就会知道暴露了哪些方法,可以对传递的对象的数据进行哪些操作。这避免了强制转换的需要,因为您指定了必须遵守的合同。

于 2012-10-20T16:25:22.830 回答
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);
    //...
}

编辑:我更喜欢李的回答。将虚拟/覆盖函数添加到类类型将是一种更好的设计和更容易处理的方式,除非 DoSomething 真的与在类中无关。

于 2012-10-20T16:22:25.073 回答
0

李是对的。让项目决定,做什么。它最清楚自己的类型,因此知道该做什么。你甚至可以给出一些标准的实现,如果它和 A 中的一样,不是抽象的,而是虚拟的。但是请注意,编译器不会要求实现。

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

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

另一种方法是使用接口。

public interface IAinterface 
{
  void DoSomething();
}

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

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

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

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

于 2013-07-29T20:09:52.880 回答