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 サービスの呼び出しごとに 1 つのメッセージを取得する代わりに、呼び出し先は 1 回の旅行ですべての間違い/成功を知ることができ、メッセージから特定の情報を解析する文字列を排除できます。

当初はジェネリックを使用することを考えていましたが、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);
    //...
}

編集:リーの答えがもっと好きです。クラス型に virtual/override 関数を追加することは、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!"}
}

もう 1 つの方法は、インターフェイスを使用することです。

public interface IAinterface 
{
  void DoSomething();
}

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

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

インターフェイスと抽象基本クラスはバックグラウンドで少し異なる動作をしますが、これは Lee の提案に似ています。

私は通常、基本クラスにいくつかの標準的な動作を与え、何か異なる場合にのみ派生クラスを実装する傾向があるため、通常は上のクラスを好みます。

于 2013-07-29T20:09:52.880 に答える