【问题标题】:Get child class properties from base class从基类获取子类属性
【发布时间】:2014-02-16 12:56:30
【问题描述】:
public abstract class Shape
{
    public String toString()
    {
        // ...
        return "";
    }
}

public class Rectangle : Shape
{
    public Double width { get; set; }
    public Double height { get; set; }
}

假设我从 Rectangle 类创建了一个对象。有没有办法通过创建的对象编写具有值的 Rectangle 类对象的属性而不覆盖 toString() 方法?


编辑:

实际上我的目的是为所有子类创建一个通用的 ToString() 方法。

我修改了我的代码

public abstract class Shape
{
    public virtual String ToString(Shape shape)
    {
        String result = String.Empty;

        foreach (var property in shape.GetType().GetProperties())
        {
            result += property.Name + " : " + property.GetValue(shape, null) + "\n";
        }

        return result;
    }
}



  public class Rectangle : Shape, IRectangle
    {
        public Double width { get; set; }
        public Double height { get; set; }

        public override String ToString()
        {
            return base.ToString(this);
        }
    }

结果:

width : 0
height : 0

但是现在,我必须为所有子类重写 ToString() 方法。我找不到此代码重复的解决方案

【问题讨论】:

  • “写 Recatangle 类对象的属性...”我不确定我是否遵循。
  • 不,不可能,因为基类对派生类一无所知。
  • 坦率地说,以其他方式来做更多的工作。
  • 执行此操作并将所有代码保留在基础中的唯一其他方法是在运行时反映 .ToString() 中的属性,但这似乎不是一个好主意。
  • 我现在看到你也标记了这个问题reflection。你的实际问题是"How to get the list of properties of a class?"

标签: c# oop reflection


【解决方案1】:

如果您只需要保留 toString 方法,您可以这样做

public abstract class Shape
{
    public String toString()
    {
        return InternalToString();
    }

    protected abstract string InternalToString();
}

public class Rectangle : Shape
{
    public Double width { get; set; }
    public Double height { get; set; }

    protected override string InternalToString()
    {
        return width.ToString() + ", " + height.ToString();
    }
}

【讨论】:

    【解决方案2】:

    您不想访问基类中的派生类,因为这样您就必须为创建的每个派生更新基类。

    如果您绝对确定不想只覆盖ToString()(您最好解释一下为什么不这样做,因为这种方法在这种情况下最有意义),您可以执行类似的操作这个:

    public abstract class Shape
    {
        public String toString()
        {
            return ShapePrinter.GetString(this);
        }
    }
    
    public static class ShapePrinter
    {
        public static string GetString(Shape shape)
        {
            if (shape is Rectangle)
            {
                return GetRectangleString(shape as Rectangle);
            }       
            if (shape is Circle)
            {
                return GetCircleString(shape as Circle);
            }
    
            throw new ArgumentException("shape");
    
        }
    
        private static string GetRectangleString(Rectangle rectangle)
        {
            return string.Format("Rectangle: {0} x {1}",
                                  rectangle.Width, rectangle.Height);
        }
    
        private static string GetCircleString(Circle circle)
        {
            return string.Format("Circle: radius {0}", circle.Radius);
        }
    }
    

    但是你实际上是在构建一个穷人的虚方法实现,你仍然必须为你添加的每种类型维护它。这与您可以使用override ToString() 完成的完全相同,因此您必须有令人信服的理由不使用它。

    【讨论】:

    • 这与每次创建新的派生类型时都必须更新基类有什么不同?
    • 你仍然需要更新它,但它在一个单独的类中。就像我说的,你不想在你的基类中知道什么类型实现了你的基类。
    • 你不希望你的基类知道派生类因为你必须更新它。出于同样的原因,您不希望 any 类知道类型的派生类型。所有这些都由 LSP 解释 - ShapePrinter 不保证适用于从Shape 派生的所有 类型。不幸的是,这是奥卡姆剃刀的情况:正确的方法是最简单的方法 - 让每个派生类型都覆盖 ToString。
    • “因为你必须更新它” - 这不是你不想要的唯一原因。我完全同意你的推理(我希望我现在的回答很清楚),如果他绝对确定他想以这种方式解决它并忽略为此而设计的机制,我只是给 OP 一些指示,比如@ 987654325@.
    • 如果任何派生类不在您的控制之下,这也根本不适用,您甚至可能不知道它们的存在。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-05
    • 1970-01-01
    • 2022-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多