【问题标题】:A way to extend existing class without creating new class in c#一种无需在 C# 中创建新类即可扩展现有类的方法
【发布时间】:2017-03-25 01:28:21
【问题描述】:

我有一个很好的完整课程,正在做很棒的事情。我需要通过替换其中的一些方法来允许用户使用这个类,但不允许继承,因为这个类在其他应用程序类中也有使用。

就像您有一个创建表格的类,但是您需要允许用户重新定义创建表格单元格的方法,以让用户在此单元格中打印自定义内容。然而,该类具有打印单元格内容的默认方式(以防用户不需要自定义它)。

是否有任何常用或标准化的方法来实现这一目标?

【问题讨论】:

  • 您能否更好地解释为什么不允许继承?你说这个类也用在其他应用类中。所以?继承不会改变基类的行为,只会改变子类。这听起来像是继承的完美候选者。
  • @adv12 因为我在这个应用程序中有 10 个使用基本类的类。继承意味着用户也需要更改其他 10 个类以让他们使用继承的类而不是原来的类
  • 不是这样。从基类继承不会对现有类的任何使用产生影响(前提是您不对基类进行任何重大更改——比如将其标记为抽象类)——这听起来像是继承的主要候选者。
  • @Epsiloncool,如果这些类正在创建它们自己的基类实例,那就是真的。但是,如果您预期所需的功能类型,通常不会这样设置应用程序。为了使这一切正常工作,您必须将对象从知道是使用基类还是子类的外部类传递到这些类中。使用对象的 10 个类不必关心它们实际上是基类还是子类的实例。
  • 将新的类对象传递给其他 10 个类是最好的解决方案,其他解决方案实际上是补丁。

标签: c# inheritance methods extending-classes


【解决方案1】:

更新

“花生画廊”指出我的方法(在底部)不符合要求,这是另一种方法:

使用委托。使用 Action 或 Func 类型定义某些公共属性。在代码中需要调用这些行为的地方,将属性与 null 进行比较。如果为 null,请使用您的默认行为。如果没有,请调用这些值。

您的调用代码可以设置属性,但不是必须的。

(第一次尝试)替代方法:

您正在描述一种扩展方法,或者如果可以使用继承。

扩展方法使您能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。扩展方法是一种特殊的静态方法,但它们被称为扩展类型的实例方法。对于用 C# 和 Visual Basic 编写的客户端代码,调用扩展方法和实际定义在类型中的方法之间没有明显区别。

https://msdn.microsoft.com/en-us//library/bb383977.aspx

继承与封装和多态一起是面向对象编程的三个主要特征(或支柱)之一。继承使您能够创建新的类来重用、扩展和修改在其他类中定义的行为。其成员被继承的类称为基类,继承这些成员的类称为派生类。派生类只能有一个直接基类。但是,继承是可传递的。如果ClassC派生自ClassB,ClassB派生自ClassA,则ClassC继承ClassB和ClassA中声明的成员。

https://msdn.microsoft.com/en-us/library/ms173149.aspx

您不能从所有 .NET 类型派生,但可以为它们编写扩展方法。

【讨论】:

  • 你可以为它们编写扩展方法 - 没错,但你不能重新定义它们的方法。
  • @Ivan 实际上,除非一个类是密封的,否则您可以通过使用覆盖。如果您对反射很方便,您甚至可以更改密封类中的方法。
  • 评论是关于扩展方法
  • @IvanStoev 如果您使用overridevirtual 方法定义,则可以。
  • @GeoffOverfield 我开始后悔发表评论。用简单的英语再说一遍,评论只是关于Extension methods
【解决方案2】:

假设您能够修改现有的类,您应该将您的方法标记为 virtual

这将允许您提供默认实现(这是您现有代码将使用的)并能够在需要时使用自定义实现覆盖它。

你的基类可能是这样的:

public class TableMaker
{
    public virtual string MakeTable()
    {
        //Provide default implementation used by existing code here
    }
}

然后您的继承类可以覆盖虚拟方法:

public class SpecialTableMaker : TableMaker
{
    public override string MakeTable()
    {
        //Provide specific implementation for cell text here
    }
}

你现有的代码可以正常工作,你可以在需要的地方使用这个其他类。

【讨论】:

  • 是的,这很简单,但我的应用程序中有 10 个其他类使用“TableMaker”类。我希望他们完全保持原样
  • @Epsiloncool 没有理由更改其他类。您在 TableMaker 上已经有一个公共方法,您所要做的就是使其成为虚拟...如果由于某种原因您无法检查我的回答
  • 如何向其他类解释使用新类(扩展)而不是旧类?
【解决方案3】:

我终于结束了这个解决方案。它是由@codenoir 提出的,但是我也有一个演示整个机制的代码。

public class MyTable
{
    public delegate string OnInsertHandler();
    public event OnInsertHandler OnInsert;


    public string Show()
    {
        string res = "-BEGIN-";
        if (OnInsert != null) {
            res += OnInsert ();
        } else {
            res += "#default insert#";
        }

        res += "-END-";

        return res;
    }
}


public class DelegateTester
{

    public void OnTest()
    {
        MyTable mt = new MyTable();

        Debug.Log("Default output: " + mt.Show());  // Shows "-BEGIN-#default insert#-END-"

        // Changing functionality via delegate
        mt.OnInsert += MyCustomInsert;

        Debug.Log("Customized output: " + mt.Show());   // Shows "-BEGIN-#custom insert#-END-"

        // Remove delegate
        mt.OnInsert -= MyCustomInsert;

        Debug.Log("Rollbacked output: " + mt.Show());   // Shows "-BEGIN-#default insert#-END-"
    }

    public string MyCustomInsert()
    {
        return "#custom insert#";
    }
}

在这个例子中,我使用的是 MyTable 类,它是使用 Func 委托扩展的。这样我就可以允许我的软件模块的用户只扩展一种方法,而不会弄乱其他类和对象。

【讨论】:

  • 这种方法首先否定了拥有类的目的。手头的问题是一个经典场景,可以而且应该通过继承和可覆盖成员来解决。提议的解决方案更多的是弥补对 OOP 概念缺乏理解的 hack。
  • @Juan 为什么你这么肯定 OOP 概念可以涵盖所有目标?我不太确定。至少我无法使用 OOP 概念解决我的初始任务。您能提出清洁且廉价的解决方案吗?
  • 我已经做了@Epsiloncool。请看下面我的回答。我很乐意进一步帮助您,但您需要分享更多代码,特别是您实际使用 MyTable 类的部分。快速的单元测试不足以了解您是如何使用它的。
猜你喜欢
  • 2023-03-21
  • 1970-01-01
  • 2013-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-02
  • 1970-01-01
  • 2012-10-22
相关资源
最近更新 更多