12

C# で、ジェネリック リストに基本クラスのみが含まれている場合、派生クラスのプロパティにアクセスする最良の方法は何ですか?

public class ClassA : BaseClass
{
   public object PropertyA { get; set; }
}

public class ClassB: BaseClass
{
    public object PropertyB { get; set; }
}

public class BaseClass
{
}

public void Main
{
    List<BaseClass> MyList = new List<BaseClass>();
    ClassA a = new ClassA();
    ClassB b = new ClassB();

    MyList.Add(a);
    MyList.Add(b);

    for(int i = 0; i < MyList.Count; i++)
    {
        //I would like to access PropertyA abd PropertyB from the derived classes        
    }
}
4

7 に答える 7

23

確かに、次のようにダウンキャストできます。

for (int i = 0; i < MyList.Count; i++)
{
    if (MyList[i] is ClassA)
    {
        var a = ((ClassA)MyList[i]).PropertyA;
        // do stuff with a
    }

    if (MyList[i] is ClassB)
    {
        var b = ((ClassB)MyList[i]).PropertyB;
        // do stuff with b
    }
}

...ただし、達成しようとしていることをもう一度確認する必要があります。ClassAとClassBのプロパティにアクセスする必要のある共通のコードがある場合は、それらのプロパティへのアクセスを祖先クラスの共有仮想プロパティまたはメソッドにラップする方がよい場合があります。

何かのようなもの:

public class BaseClass
{
    public virtual void DoStuff() { }
}

public class ClassA : BaseClass
{
    public object PropertyA { get; set; }

    public override void DoStuff() 
    {
        // do stuff with PropertyA 
    }
}

public class ClassB : BaseClass
{
    public object PropertyB { get; set; }

    public override void DoStuff() 
    {
        // do stuff with PropertyB
    }
}
于 2008-10-16T23:20:55.063 に答える
5

TimJの答えに加えて、すべてのタイプで機能する1つの拡張メソッドを記述できます。

public static IEnumerable<T> OfType<T>(this IEnumerable list)
{
    foreach (var obj in list)
    {
        if (obj is T)
            yield return (T)obj;
    }
}

または、Linqがある場合、その関数は名前空間System.Linqにあります。

于 2008-10-17T11:20:21.587 に答える
2
   BaseClass o = MyList[i];
   if (o is ClassB)
   {
      object k = ((ClassB)o).PropertyB;
   }
   if (o is ClassA))
   {
      object j = ((ClassA)o).PropertyA;
   }
于 2008-10-16T23:20:24.957 に答える
2

これを頻繁に行う場合は、リストに拡張メソッドを作成して、正しく型付けされた列挙を返すという別のオプションがあります。すなわち

  public static class MyBaseListExtensions
  {
    public static IEnumerable<ClassA> GetAllAs(this List<MyBaseClass> list)
    {
      foreach (var obj in list)
      {
        if (obj is ClassA)
        {
          yield return (ClassA)obj;
        }
      }
    }

    public static IEnumerable<ClassB> GetAllbs(this List<MyBaseClass> list)
    {
      foreach (var obj in list)
      {
        if (obj is ClassB)
        {
          yield return (ClassB)obj;
        }
      }
    }
  }

次に、次のように使用できます....

private void button1_Click(object sender, EventArgs e)
{
  ClassA a1 = new ClassA() { PropertyA = "Tim" };
  ClassA a2 = new ClassA() { PropertyA = "Pip" };
  ClassB b1 = new ClassB() { PropertyB = "Alex" };
  ClassB b2 = new ClassB() { PropertyB = "Rachel" };

  List<MyBaseClass> list = new List<MyBaseClass>();
  list.Add(a1);
  list.Add(a2);
  list.Add(b1);
  list.Add(b2);

  foreach (var a in list.GetAllAs())
  {
    listBox1.Items.Add(a.PropertyA);
  }

  foreach (var b in list.GetAllbs())
  {
    listBox2.Items.Add(b.PropertyB);
  }
}
于 2008-10-17T00:26:07.850 に答える
1

前提全体が意味をなさない - PropertyB はインスタンスの場合はどうなるでしょうか?

手動で実行時の型チェック (inst は Foo) を行ってから、必要なプロパティを持つ型にキャストすると、これを行うことができます。

于 2008-10-16T23:19:17.580 に答える
0

Genericsとサブクラスに問題がある可能性があります(その場合はSystem.Collections.ArrayListに戻る必要があります)が、使用するサブクラスにBaseClassをキャストする必要があります。'as'ディレクトリを使用する場合、BaseClassをサブクラスにキャストできる場合は成功し、キャストできない場合はnullになります。次のようになります。

for(int i = 0; i < MyList.Count; i++)
{
    BaseClass bc = MyList[i];
    ClassA a = bc as ClassA;
    ClassB b = bc as ClassB;
    bc.BaseClassMethod();
    if (a != null) {
       a.PropertyA;
    }
    if (b != null) {
       b.PropertyB;
    }
}

また、これは少し悪臭がします。これは、不十分な構造のオブジェクト階層を示す種類のコードです。一般に、IS A BaseClassと言えない場合は、設計が間違っている可能性があります。しかし、それがお役に立てば幸いです。

于 2008-10-16T23:20:49.183 に答える
0

基本クラスでプロパティを仮想として宣言し、派生クラスでそれらをオーバーライドする必要があります。

元:

public class ClassA : BaseClass
{
   public override object PropertyA { get; set; }
}

public class ClassB: BaseClass
{
    public override object PropertyB { get; set; }
}

public class BaseClass
{
    public virtual object PropertyA { get; set; }
    public virtual object PropertyB { get; set; }
}

public void Main
{
    List<BaseClass> MyList = new List<BaseClass>();
    ClassA a = new ClassA();
    ClassB b = new ClassB();

    MyList.Add(a);
    MyList.Add(b);

    for(int i = 0; i < MyList.Count; i++)
    {
     // Do something here with the Property
        MyList[i].PropertyA;
        MyList[i].PropertyB;      
    }
}

基本クラスにプロパティを実装してデフォルト値 (null など) を返すか、抽象化してすべての派生クラスに両方のプロパティを実装させる必要があります。

また、両方の派生クラスでそれをオーバーライドし、異なる値を返すことで、たとえば PropertyA に対して異なるものを返すことができることに注意してください。

于 2008-10-17T00:28:35.007 に答える