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

Vai*_*ain 43 .net c# oop

问题标题似乎有点混乱,但我会在这里试着澄清我的问题.

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

    }
}
Run Code Online (Sandbox Code Playgroud)

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

  1. Employee是一个Abstract类,它有一个抽象方法GiveBonus
  2. SalesPerson来自Employee.SalesPerson必须给抽象方法GiveBonus定义.(SalesPerson不能是抽象的)
  3. PTSalesPerson派生自SalesPerson.

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

Eri*_*ert 95

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

这就是抽象方法要解决的问题:你有一个所有派生类共有的方法但没有合理的基类实现,而不是"我需要一种方法来强制我的派生类型提供实现".这派生类型被迫提供一个实现是一个后果的的解决方案,而不是问题的打算首先得到解决.

C#语言没有问题的机制"我必须强制我的子类型提供他们自己的这种方法的实现",因为根据我的知识,语言设计者所考虑的问题不会成为大多数人的问题.我们的顾客.

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

override void M() { base.M(); }
Run Code Online (Sandbox Code Playgroud)

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

但更一般地说:我不确定您的层次结构是否首先是合理设计的.当我在员工身上看到GiveBonus的方法时,我认为这意味着"员工可以给予奖金",而不是"员工可以获得奖金".当然,经理会给予奖金,而员工获得奖金.我想你可能会让员工层次结构做得太多.

  • 对于这样的强制重写,Equals听起来像是一个很好的候选者.我不希望人们忘记他们需要覆盖相等,否则他们的派生类添加的额外字段将不会被比较. (4认同)
  • @ROX:我明白你的意思了.我们还经常看到此错误的"双重":有人重写方法并*忘记*调用方法的基类版本,即使它们没有在覆盖中充分复制其逻辑.而不是*必须覆盖*,我想看到表达方式*must-call-base-if-you-override*. (4认同)
  • 啊,不,我在一些事情上是不正确的; 我没有完全理解问题的背景和解释,我对"C#语言没有......的机制"作出了反应.我基于对"C#MustInherit"的简单搜索来到这个Q&A,因此这是我的困惑和反对的借口.C#确实具有MustOveride等价物(机制),抽象类上的抽象方法.. http:/ /msdn.microsoft.com/en-us/library/k535acbf(v=vs.71).aspx ..当然,这不是讨论的重点. (2认同)
  • (我知道这是一个老问题-但对我来说是当前的问题),正如克莱门特所说,Equals是一个明智的用例。克隆将是另一个-还有更多。是的,这是派生类设计人员的选择,即基础实现是否能够完成工作,并且没有任何东西-并且也不需要任何东西-阻止他们按您所示的方式调用基本实现。但是,这是派生类开发人员的有意识的决定。必须覆盖的机制将确保他们不会忘记考虑是否需要覆盖。 (2认同)

ang*_*son 10

除非您将SalesPerson抽象化或更改层次结构,否则您不能这样做.

怎么样:

                       Employee*
                           ^
                           |
                       SalesPersonBase* (have all the code except GiveBonus)
                        ^           ^
                        |           |
                 SalesPerson      PTSalesPerson
Run Code Online (Sandbox Code Playgroud)

Employee和SalesPersonBase现在都标记为抽象.

但是,如果您需要PTSalesPerson不仅继承行为,还继承is-a关系(PTSalesPerson也是SalesPerson),那么您无法强制执行此操作.

注意,只有在您考虑编译时检查时,上述文本才有效.换句话说,如果您希望编译器在没有向PTSalesPerson类添加覆盖的情况下进行投诉,则不能这样做,除非您执行上面概述的操作.

但是,没有什么能阻止你在运行时使用反射来检查方法,并且如果PTSalesPerson中的方法没有在那里显式覆盖,则抛出异常,但是我会认为这是一个hack.


Jua*_*nZe 7

将类声明Employee为抽象,但提供和实现GiveBonus()会抛出运行时异常,并带有"必须由子类实现"之类的消息.这是一个古老的Smalltalk练习......不确定它对C#是否有用.我在Java代码中使用过它.


Rob*_*ney 7

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

public abstract class BonusCalculator
{
   public abstract decimal CalculateBonus(Employee e)
}
Run Code Online (Sandbox Code Playgroud)

在你的基类:

private BonusCalculator Calculator { get; set; }

public void GiveBonus()
{
   Bonus = Calculator.CalculateBonus(this)
}
Run Code Online (Sandbox Code Playgroud)

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

public SomeKindOfEmployee()
{
    Calculator = new SomeKindOfEmployeeBonusCalculator();
}
Run Code Online (Sandbox Code Playgroud)

实现Person子类的人现在必须明确地为它提供a的实例BonusCalculator(或者NullReferenceExceptionGiveBonus方法中获取).

作为一个额外的,呃,奖金,这种方法允许不同的子类Person共享奖金计算方法,如果这是合适的.

编辑

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


小智 5

WA可能正在使用"接口",因此您可以定义一个类似IBonusGiver {void GiveBonus(); 然后,在所有类中实现这个新接口,而不是抽象方法和覆盖.例如PTSalesPerson:SalesPerson,IBonusGiver在每个类中强制执行新的实现.


Vin*_*ent 5

这是一个非常老的话题,但是对此问题给出的答案可能是有用的。您可以强制派生类具有自己的虚拟方法/属性实现。

public class D
{
    public virtual void DoWork(int i)
    {
        // Original implementation.
    }
}

public abstract class E : D
{
    public abstract override void DoWork(int i);
}

public class F : E
{
    public override void DoWork(int i)
    {
        // New implementation.
    }
}
Run Code Online (Sandbox Code Playgroud)

如果将虚拟方法声明为抽象方法,则对于从该抽象类继承的任何类,该方法仍然是虚拟的。继承抽象方法的类无法访问该方法的原始实现-在上一示例中,类F上的DoWork无法在类D上调用DoWork。这样,抽象类可以强制派生类为虚拟方法提供新的方法实现。 。