【问题标题】:Accessing a property of derived class from the base class in C#从 C# 中的基类访问派生类的属性
【发布时间】:2008-10-16 23:11:54
【问题描述】:

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

【问题讨论】:

    标签: c# generics collections


    【解决方案1】:

    当然,你可以像这样沮丧:

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

    【讨论】:

    • 我使用第二个代码示例来访问派生类中的属性......感谢您的输入。
    • 这太棒了。我到处搜索,发现很多线程说这是不可能的,但这个解决方案绝对是一种享受。
    • 我一定遗漏了一些东西,但是如果您只是要在派生类中编写代码,为什么还需要BaseClass 中的DoStuff()?你不会只使用一个接口来强制在派生类中包含DoStuff() 吗?
    • @SteveCinq 因为这样,您可以调用 BaseClass 对象上的方法,而无需知道该类的确切派生类型。
    【解决方案2】:

    除了 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 中。

    【讨论】:

      【解决方案3】:
         BaseClass o = MyList[i];
         if (o is ClassB)
         {
            object k = ((ClassB)o).PropertyB;
         }
         if (o is ClassA))
         {
            object j = ((ClassA)o).PropertyA;
         }
      

      【讨论】:

        【解决方案4】:

        如果您经常这样做,另一种选择是在列表上创建一个扩展方法,以返回正确键入的枚举。即

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

        【讨论】:

          【解决方案5】:

          整个前提没有意义——对于 a 实例,PropertyB 是什么?

          如果您进行手动运行时类型检查(inst 为 Foo),则可以执行此操作,然后转换为具有所需属性的类型。

          【讨论】:

            【解决方案6】:

            您可能对泛型和子类有一些问题(在这种情况下,您应该返回 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,你的设计可能是错误的。但是,希望对您有所帮助!

            【讨论】:

              【解决方案7】:

              您需要在基类上将属性声明为虚拟属性,然后在派生类中覆盖它们。

              例如:

              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 并返回不同的值来返回不同的内容。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-12-07
                • 2010-09-21
                • 2018-03-21
                相关资源
                最近更新 更多