Доступ к свойству производного класса из базового класса в C # - PullRequest
9 голосов
/ 17 октября 2008

В 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        
    }
}

Ответы [ 7 ]

19 голосов
/ 17 октября 2008

Конечно, вы можете опускаться, вот так:

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
    }
}
5 голосов
/ 17 октября 2008

В дополнение к ответу TimJ, вы можете написать один метод расширения, который будет работать для всех типов:

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.

2 голосов
/ 17 октября 2008

Если вы делаете это много, другой вариант - создать метод расширения в списке, чтобы вернуть вам правильно набранное перечисление. т.е.

  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);
  }
}
1 голос
/ 17 октября 2008
   BaseClass o = MyList[i];
   if (o is ClassB)
   {
      object k = ((ClassB)o).PropertyB;
   }
   if (o is ClassA))
   {
      object j = ((ClassA)o).PropertyA;
   }
1 голос
/ 17 октября 2008

Вся предпосылка не имеет смысла - что будет PropertyB для экземпляра?

Вы можете сделать это, если выполняете ручную проверку типов во время выполнения (например, Foo), а затем приводите к типу с требуемым свойством.

0 голосов
/ 17 октября 2008

В базовом классе необходимо объявить свойства как виртуальные, а затем переопределить их в производном классе.

Ex:

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, переопределив его в обоих производных классах и вернув разные значения.

0 голосов
/ 17 октября 2008

У вас могут быть некоторые проблемы с Generics и подклассами (в этом случае вы должны вернуться к System.Collections.ArrayList), но вы должны привести BaseClass к подклассу, который вы хотите использовать. Если вы используете каталог «as», он будет успешным, если базовый класс может быть приведен к подклассу, или он будет нулевым, если он не может быть приведен. Это будет выглядеть примерно так:

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, ваш дизайн, вероятно, не так. Но, надеюсь, это поможет!

...