C# 如何在没有抽象基类的情况下强制覆盖后代中的方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2118055/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 23:39:39  来源:igfitidea点击:

How to force overriding a method in a descendant, without having an abstract base class?

c#.netoop

提问by Vaibhav Jain

Question Heading seems to be little confusing, But I will Try to clear my question here.

问题标题似乎有点令人困惑,但我会尝试在这里澄清我的问题。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    public abstract class Employee
    {
        private string name;
        private int empid;
        BenefitPackage _BenefitPackage = new BenefitPackage();
        public string Name
         {
             get { return this.name; }
             set { this.name = value; }
            }
        public int EmpId
        {
            get { return this.empid; }
            set
            {
                if (value == 1)
                    return;
                this.empid = value; }
        }
        public Employee(string Name, int EmpId)
        {
            this.Name = Name;
            this.EmpId = EmpId;
        }
        public Employee()
        { }

        public abstract void GiveBonus();

    }

    public class Manager : Employee
    {
        private int noofstockoptions;
        public override void GiveBonus()
        {
            Console.WriteLine("Manger GiveBonus Override");
        }
        public int NoOfStockOptions
        {
            get { return this.noofstockoptions; }
            set { this.noofstockoptions = value; }
        }

        public Manager(string Name,int EmpId, int NoOfStockOptions):base(Name,EmpId)
        {
            this.NoOfStockOptions=NoOfStockOptions;
        }

    }
    public class SalesPerson:Employee
    {
        private int noofsales;
        public int NoOfSales
        {
            get { return this.noofsales; }
            set { this.noofsales = value; }
        }

        public SalesPerson(string Name, int EmpId, int NoOfSales):base(Name,EmpId)
        {
            this.NoOfSales = NoOfSales;
        }
        public override void GiveBonus()
        {
            Console.WriteLine("Hi from salesperson");
        }
    }
    public sealed class PTSalesPerson : SalesPerson
    {
        private int noofhrworked;
        public int NoOfHrWorked
        {
            get { return this.noofhrworked; }
            set { this.noofhrworked = value; }

        }
        public PTSalesPerson(string Name, int EmpId, int NoOfSales,int NoOfHrWorked):base(Name,EmpId,NoOfSales)
        {
            this.NoOfHrWorked = NoOfHrWorked;

        }
        //public new void GiveBonus()
        //{
        //    Console.WriteLine("hi from ptsalesperson");
        //} 
    }

    class BenefitPackage
    {
        public int Bonus;
        public int GiveBonus()
        {
            int i = 200;
            return i;
        }

        private class innerPublic
        {
            public int innerBonus;

        }


    }

    class MainClass
    {
        public static void Main()
        { 
        Manager _Manager=new Manager("Vaibhav",1,50);
        PTSalesPerson _PTSalesPerson = new PTSalesPerson("Shantanu", 1, 4, 6);
        _Manager.GiveBonus();

        Employee _emp;
        //_emp = new Employee("new emp",4);
        //_emp.GiveBonus();
        _PTSalesPerson.GiveBonus();
        ((SalesPerson)_PTSalesPerson).GiveBonus();
        Console.ReadLine();    
        }

    }
}

Please do not try to understand the whole code.I am summarising it.

请不要试图理解整个代码。我正在总结它。

  1. Employee is a Abstract class, which have an abstract method GiveBonus
  2. SalesPerson is a deriving from Employee. SalesPerson has to give definition to abstract Method GiveBonus.(SalesPerson can not be Abstract)
  3. PTSalesPerson is deriving from SalesPerson.
  1. Employee 是一个抽象类,它有一个抽象方法 GiveBonus
  2. SalesPerson 是从 Employee 派生的。销售人员必须定义抽象方法 GiveBonus。(销售人员不能是抽象的
  3. PTSalesPerson 派生自 SalesPerson。

Now my question is, How can I forcePTSalesPerson to have its own implementation of GiveBonus.

现在我的问题是,如何强制PTSalesPerson 拥有自己的 GiveBonus 实现。

采纳答案by Eric Lippert

I think you're thinking about this the wrong way. The language designers did not say to themselves "what we really need is a way to mark a method as must be overridden, let's invent this thing called abstract". They said "A virtual method lets us represent the idea that every derived type of this base type should be able to do this method. But what if there is no sensible codethat can possibly go in the base class version of the method? I know, let's invent this thing called an abstract method for that circumstance."

我认为你正在以错误的方式思考这个问题。语言设计者并没有对自己说“我们真正需要的是一种将方法标记为必须被覆盖的方法,让我们发明一个叫做抽象的东西”。他们说“虚拟方法让我们表示这种基类型的每个派生类型都应该能够执行此方法的想法。但是如果没有可以在该方法的基类版本中使用的合理代码怎么办?我知道,让我们为这种情况发明一种叫做抽象方法的东西。”

That's the problem that abstract methods were intended to solve: you have a method common to all derived classes but no sensible base class implementation, NOT "I need a way to force my derived types to provide an implementation". That derived types are forced to provide an implementation is a consequenceof the solution, but not the problemintended to be solved in the first place.

这就是抽象方法要解决的问题:您有一个所有派生类通用的方法,但没有合理的基类实现,而不是“我需要一种方法来强制我的派生类型提供实现”。这派生类型被迫提供一个实现是一个后果的的解决方案,而不是问题的意图首先要解决的。

The C# language does not have a mechanism for the problem "I must force my subtype to provide their own implementation of this method" because that's not a problem that the language designers, to my knowledge, ever considered would be a problem for the majority of our customers.

C# 语言没有解决“我必须强制我的子类型提供他们自己的此方法实现”问题的机制,因为据我所知,这不是语言设计者曾经认为对大多数人来说是一个问题的问题。我们的顾客。

So my question to you is: why do you want to do this? Surely it is up to the developer of the derived class to determine whether or not the base class implementation is correct for the derived class or not. That's not up to you. And even if you did have some way to do that, what would stop the developer from simply saying

所以我要问你的问题是:你为什么要这样做?当然,由派生类的开发人员来确定基类实现对于派生类是否正确。这不取决于你。即使你确实有办法做到这一点,有什么能阻止开发人员简单地说

override void M() { base.M(); }

?

?

Can you explain what purpose you have for attempting to force this work upon the developers of your derived classes? Perhaps there is a better way to achieve what you want.

您能解释一下您试图将这项工作强加给您的派生类的开发人员的目的是什么吗?也许有更好的方法来实现你想要的。

But more generally: I am not sure that your hierarchy is sensibly designed in the first place. When I see a method GiveBonus on an Employee, I assume that this means that "an employee can give a bonus", not "an employee can receive a bonus". Surely a managergives a bonus and an employee receivesa bonus. I think you might be making the employee hierarchy do too much.

但更一般地说:我不确定你的层次结构首先是明智地设计的。当我在员工上看到 GiveBonus 方法时,我认为这意味着“员工可以发放奖金”,而不是“员工可以领取奖金”。当然,经理会给奖金,而员工会收到奖金。我认为您可能让员工等级制度做得太多。

回答by Lasse V. Karlsen

You can not, unless you make SalesPerson abstract or change the hierarchy.

您不能,除非您使 SalesPerson 抽象或更改层次结构。

How about:

怎么样:

                       Employee*
                           ^
                           |
                       SalesPersonBase* (have all the code except GiveBonus)
                        ^           ^
                        |           |
                 SalesPerson      PTSalesPerson

Both Employee and SalesPersonBase are now marked as abstract.

Employee 和 SalesPersonBase 现在都被标记为抽象的。

However, if you require a PTSalesPerson to not only inherit behavior, but also inherit the is-a relationship (a PTSalesPerson is also a SalesPerson), then you have no way of forcing this.

但是,如果您要求 PTSalesPerson 不仅继承行为,还继承 is-a 关系(PTSalesPerson 也是 SalesPerson),那么您无法强制这样做。

Note, the above text is only valid if you only consider compile-time checks. In other words, if you want the compiler to complain if you haven't added an override to the PTSalesPerson class, you cannot do that, unless you do what I outlined above.

请注意,以上文本仅在您仅考虑编译时检查时才有效。换句话说,如果您希望编译器在您没有向 PTSalesPerson 类添加覆盖时发出警告,则不能这样做,除非您执行我上面概述的操作。

However, there's nothing stopping you from using reflection to examine the methods at runtime, and throw an exception if the method in PTSalesPerson is not explicitly overridden there, however I would consider that a hack.

但是,没有什么可以阻止您在运行时使用反射来检查方法,如果 PTSalesPerson 中的方法没有在那里显式覆盖,则抛出异常,但我认为这是一种黑客攻击。

回答by hackerhasid

You can't using the setup you described. PTSalesPerson will already have an implementation of GiveBonus because it inherits from SalesPerson.

您不能使用您描述的设置。PTSalesPerson 已经有 GiveBonus 的实现,因为它继承自 SalesPerson。

回答by Lee

If you aren't going to create a SalesPerson instance directly, then the simplest thing to do would be to make SalesPerson abstract and that would force any child classes to implement it instead (or be abstract themselves).

如果您不打算直接创建 SalesPerson 实例,那么最简单的做法是将 SalesPerson 抽象化,这将强制任何子类改为实现它(或者自己抽象化)。

If there is some logic in that method common to all SalesPeople, then you could implement GiveBonus on SalesPerson as a template method. Which calls some abstract method required in any subclasses:

如果该方法中有一些对所有 SalesPeople 通用的逻辑,那么您可以在 SalesPerson 上实现 GiveBonus 作为模板方法。它调用了任何子类中所需的一些抽象方法:

Which ever way you decide, the only way to force an implementation in a child class is to make the base class abstract.

无论您决定哪种方式,在子类中强制实现的唯一方法是使基类抽象。

回答by JuanZe

Declare the class Employeeas abstract but provide and implementation of GiveBonus()that throws a runtime exception with a message like "Must be implemented by subclasses". It's an old Smalltalk practice... Not sure if it is useful to C#. I have used it in Java code.

将类声明Employee为抽象类,但提供并实现该类GiveBonus()会引发运行时异常,并带有类似“必须由子类实现”的消息。这是一个旧的 Smalltalk 实践......不确定它是否对 C# 有用。我在 Java 代码中使用过它。

回答by FallenAvatar

The only way i can see of making this work, if you can not make SalesPerson Abstract, is this:

如果你不能让销售人员抽象,我能看到的唯一方法是:

1) in SalesPerson.GiveBonus(...) use reflection to determine if 'this' is a SalesPerson, or a derived class a) if not a derived class, do current code in SalesPerson.GiveBonus b) otherwise call GiveBonusDerived. (declare this as virtual, and make the implmentation in SalesPerson throw an exception.)

1) 在 SalesPerson.GiveBonus(...) 中使用反射来确定“this”是 SalesPerson 还是派生类 a) 如果不是派生类,则在 SalesPerson.GiveBonus 中执行当前代码 b) 否则调用 GiveBonusDerived。(将其声明为虚拟,并使 SalesPerson 中的实现抛出异常。)

The draw backs here are, Reflection is slow. No compile time error if GiveBonusDerived isn't declared, etc.

这里的缺点是,反射很慢。如果未声明 GiveBonusDerived 等,则不会出现编译时错误。

回答by Robert Rossney

Use dependency injection. Create a BonusCalculatorclass:

使用依赖注入。创建一个BonusCalculator类:

public abstract class BonusCalculator
{
   public abstract decimal CalculateBonus(Employee e)
}

In your base class:

在您的基类中:

private BonusCalculator Calculator { get; set; }

public void GiveBonus()
{
   Bonus = Calculator.CalculateBonus(this)
}

In your implementation's constructor:

在您的实现的构造函数中:

public SomeKindOfEmployee()
{
    Calculator = new SomeKindOfEmployeeBonusCalculator();
}

Someone implementing a Personsubclass now has to explicitly provide it with an instance of a BonusCalculator(or get a NullReferenceExceptionin the GiveBonusmethod).

实现Person子类的人现在必须显式地为其提供 a 的实例BonusCalculator(或NullReferenceExceptionGiveBonus方法中获取 a )。

As an added, er, bonus, this approach allows different subclasses of Personto share a bonus-calculation method if that's appropriate.

作为额外的,呃,奖励,Person如果合适的话,这种方法允许不同的子类共享奖励计算方法。

Edit

编辑

Of course, if PTSalesPersonderives from SalesPersonand its constructor calls the base constructor, this won't work either.

当然,如果PTSalesPerson派生自SalesPerson并且它的构造函数调用了基构造函数,这也不起作用。

回答by Stephen

You could always make SalesPerson's implementation throw a NotImplementedException. :V But contractually, no, you can't do that.

您总是可以让 SalesPerson 的实现抛出 NotImplementedException。:V 但根据合同,不,你不能那样做。

回答by Dave Cousineau

I feel like this indicates that your implementation really has two parts: a partial implementation that goes in the base class, and a missing completion to that implementation that you want in the sub class. Ex:

我觉得这表明您的实现确实有两个部分:基类中的部分实现,以及子类中所需的该实现的缺失完成。前任:

public abstract class Base
{
   public virtual void PartialImplementation()
   {
      // partial implementation
   }
}

public sealed class Sub : Base
{
   public override void PartialImplementation()
   {
      base.PartialImplementation();
      // rest of implementation
   }
}

You want to force the override, since the initial implementation is incomplete. What you can do is split your implementation into two parts: the implemented partial part, and the awaiting-implementation missing part. Ex:

您想强制覆盖,因为初始实现不完整。您可以做的是将您的实现分为两部分:已实现的部分和等待实现的缺失部分。前任:

public abstract class Base
{
   public void PartialImplementation()
   {
      // partial implementation

      RestOfImplementation();
   }

   // protected, since it probably wouldn't make sense to call by itself
   protected abstract void RestOfImplementation();      
}

public sealed class Sub : Base
{
   protected override void RestOfImplementation()
   {
      // rest of implementation
   }
}

回答by j2associates

This is pretty old, but we have a somewhat similar situation. The base class loads a configuration file and sets up base class defaults. However, the configuration file could also contain default values for inherited classes.

这已经很老了,但我们的情况有些相似。基类加载配置文件并设置基类默认值。但是,配置文件也可以包含继承类的默认值。

This is how we got the combined functionality.

这就是我们如何获得组合功能。

Base class methods

基类方法

private void processConfigurationFile()
{
    // Load and process the configuration file
    // which happens to be an xml file.
    var xmlDoc = new XmlDocument();
    xmlDoc.Load(configurationPath);

    // This method is abstract which will force
    // the inherited class to override it.
    processConfigurationFile(xmlDoc);
}

protected abstract void processConfigurationFile(XmlDocument document);