【问题标题】:Visibility of properties in derived classes (C#)派生类中属性的可见性 (C#)
【发布时间】:2011-11-26 23:01:52
【问题描述】:

我一直在尝试将正确的 OOP 原则应用于我的项目。我有一个名为 DocumentSection 的抽象类,以及从它派生的几个类(DocumentSectionView、DocumentSectionText 等)。同样,我有一个抽象类(DocAction),其中有几个派生自它的类(DocumentActionReplaceByTag、DocumentSectionAppend 等)。每个 DocumentSection 中都有一个 DocumentAction。

我对所有这些继承业务的理解是,通过指定一个“DocumentAction”,这将允许将任何派生类放在它的位置,并且基类的任何属性/方法也将可用正如我实例化的具体类中指定的任何内容。因此,在下面的示例中,我希望能够看到 PerformAction 方法(暂时将 virtual/override 关键字排除在外)。它是可用的。

但是,因为我使用了 v.DocAction = new DocumentActionReplaceByTag();,所以我还希望我的 ReplaceActionFindText 属性可见。

显然我在某处弄错了 - 任何 cmets 都表示赞赏。

class Program
{
    static void Main(string[] args)
    {
        DocumentSectionView v = new DocumentSectionView();
        v.DocAction = new DocumentActionReplaceByTag();

        // would like to go:
        //v.DocAction.ReplaceActionFindText...

        Console.ReadLine();
    }   
}    
public abstract class DocumentSection
{
    public abstract string GetContent();
    public DocumentAction DocAction { get; set; }
}
public class DocumentSectionView : DocumentSection
{
    public string ViewPath { get; set; }
    public dynamic ViewModel { get; set; }

    public override string GetContent()
    {
        return "test";
    }
}    
public abstract class DocumentAction
{
    void PerformAction(StringBuilder sb, string content);
}
public class DocumentActionReplaceByTag : DocumentAction
{
    public string ReplaceActionFindText { get; set; }
    public void PerformAction(StringBuilder sb, string content)
    {
        sb.Replace(ReplaceActionFindText, content);
    }
}

编辑: 我已将答案标记为正确,但我想为以后遇到此问题的人添加我对此事的进一步思考的成果:

a) 正如所指出的,我的意图大体上是正确的,但我的方法是错误的。从 Main 方法设置“Action”属性不正确。在所有情况下,DocumentActionReplaceByTag 都需要 FindText,所以我将它放在构造函数中:

    public DocumentActionReplaceByTag(string replaceActionFindText)
    {
        this.ReplaceActionFindText = replaceActionFindText;
    }

从那时起,带有 0 个参数的构造函数将正确地失败,并防止执行操作但未指定 findtext 的情况。

b) 多态性现在可以正常工作,因为我的额外属性 findtext 已被填充,并且无论操作类型如何,运行 PerformAction 都会正确运行。

【问题讨论】:

  • 谢谢大家。那么,在 DocumentSection 中指定可以指定任何类型的 DocAction 的“OOP 正确”方式是什么。目标是从我的旧方法(在stackoverflow.com/questions/8242520/… 讨论)转移到使用多态调用 action.GetContent();在我的 DocAction 上运行任何适当的 getcontent 操作。但要做到这一点,我需要设置特定于该操作的属性(如 ReplaceActionText)。下面的演员表被评论为不理想 - 演员表是唯一的方法吗?

标签: c# inheritance properties abstract-class


【解决方案1】:

因为您将派生类分配给具有基类类型的属性,所以只有基类的方法和属性可用。这是有道理的,因为您可以分配从基类派生的类的 任何 实例 - 因此任何派生方法都不能在此上下文中使用。

这是 OOP 原则之一 - 您的派生类实例可以用作基类的实例(但不能反过来)

编辑:

详细说明@sll 提出的转换为特定派生类类型的解决方案:不要这样做!这是一种解决方法,但不符合整体设计的利益。

如果您必须转换为派生类型,那么您就违反了Liskov substitution principle,这意味着任何派生类型都应该可以用来代替基类型——如果您需要特定的转换,显然情况并非如此。

重新考虑您的设计 - 您真的需要一个具有基类类型的属性吗?如果需要,那么目前仅在一种特定派生类型中的方法是否也最好是在基类型中?

【讨论】:

  • 感谢碎玻璃。在上面的链接 (stackoverflow.com/questions/8242520/…) 中,我试图避免拥有与给定目的无关的属性。例如,一个动作可能需要一个路径/文件名,而另一个动作可能需要一个“文本内容”属性。将它们捆绑到基类中听起来不像“正确的方法”?谢谢
【解决方案2】:

v 引用类型属于DocumentSectionView,它不知道DocumentActionReplaceByTag 类的方法,即使底层实例属于DocumentActionReplaceByTag,因为你已经分配了它。您需要对其进行强制转换才能访问派生类成员:

((DocumentActionReplaceByTag)v.DocAction).ReplaceActionFindText

此外,在某些情况下,当无法强制转换底层实例时这很好,因此应该跳过某些代码部分,然后您可以使用 as operator 使用异常安全的强制转换方式:

var typedAction = v.DocAction as DocumentActionReplaceByTag;
if (typedAction != null)
{
   // accessing the typedAction.ReplaceActionFindText property
}

我的建议只是为了帮助您理解 C# 方面的问题,关于整体设计和方法,请参阅 BrokenGlass 的回答。

【讨论】:

  • +1 指出演员可以让他做他想做的事
  • 这会破坏这个设计的目的
  • @BrokenGlass :抱歉,我没有理解当前设计的重点,您能澄清一下吗?这种设计的正确解决方案是什么?我只是看到 main() 方法中的代码没有意义,因为在将派生类实例分配给基引用时没有使用 OOP 的好处
  • 如果他有一个基类类型的属性,您永远不必强制转换为更派生的类类型 - 这违反了 Liskov 替换原则,并且通常是代码味道(说温和)
  • 我想了很多,并阅读了一些关于 Liskov 替换原则的内容。意思是子类 S 应该能够毫无问题地替换基类 C。在我看来,在 S 中拥有额外的属性或方法并不会破坏这一原则——我实际上认为这是拥有子类的原因?
【解决方案3】:

不,在您的示例中,由于 DocAction 只是 DocumentAction,因此无论使用哪种派生类型的 DocumentAction,您都只能看到 DocumentAction 的属性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-06-10
    • 2010-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-01
    • 1970-01-01
    相关资源
    最近更新 更多