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

时间:2010-01-22 14:57:22

标签: c# .net 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();    
        }

    }
}

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

  1. Employee是一个Abstract类,它有一个抽象方法GiveBonus
  2. SalesPerson来自Employee。 SalesPerson必须给抽象方法GiveBonus定义。( SalesPerson不能抽象
  3. PTSalesPerson派生自SalesPerson。
  4. 现在我的问题是,我如何强制 PTSalesPerson拥有自己的GiveBonus实现。

15 个答案:

答案 0 :(得分:93)

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

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

C#语言没有问题的机制“我必须强制我的子类型提供他们自己的这种方法的实现”,因为根据我的知识,语言设计者认为这不是一个问题。我们的大多数客户。

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

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

你能解释一下你试图在派生类的开发者身上强迫这项工作的目的吗?也许有更好的方法来实现你想要的东西。

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

答案 1 :(得分:10)

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

怎么样:

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

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

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

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

但是,没有什么能阻止你在运行时使用反射检查方法,并且如果PTSalesPerson中的方法没有在那里明确地重写,则抛出异常,但是我认为这是一个hack。

答案 2 :(得分:7)

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

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

在您的基类中:

private BonusCalculator Calculator { get; set; }

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

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

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

实现Person子类的人现在必须明确地为其提供BonusCalculator的实例(或在NullReferenceException方法中获得GiveBonus)。

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

修改

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

答案 3 :(得分:6)

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

答案 4 :(得分:5)

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

答案 5 :(得分:4)

这是一个非常古老的主题,但this question给出的答案可能很有用。 您可以强制派生类具有自己的虚方法/属性实现。

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.
    }
}
  

如果虚拟方法被声明为abstract,则它仍然是虚拟方法   继承自抽象类的类。继承一个类   抽象方法无法访问原来的实现   方法 - 在前面的例子中,F类上的DoWork无法调用DoWork   在类D上。这样,抽象类可以强制派生类   为虚拟方法提供新的方法实现。

答案 6 :(得分:2)

我觉得这表明您的实现实际上有两个部分:基类中的部分实现,以及您希望在子类中实现的实现缺失。例如:

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

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

您希望强制覆盖,因为初始实现不完整。您可以做的是将实现分为两部分:实现的部分部分和等待实现缺失部分。例如:

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
   }
}

答案 7 :(得分:1)

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

答案 8 :(得分:1)

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

如果所有SalesPeople中的该方法都有一些共同的逻辑,那么您可以将SalesPerson上的GiveBonus实现为template method。其中调用任何子类中所需的抽象方法:

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

答案 9 :(得分:1)

如果你不能制作SalesPerson Abstract,我能看到做这项工作的唯一方法就是:

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

这里的缺点是,反思很慢。如果未声明GiveBonusDerived等,则无编译时错误

答案 10 :(得分:0)

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

答案 11 :(得分:0)

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

这就是我们获得组合功能的方式。

基类方法

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

答案 12 :(得分:0)

好的,我知道这篇文章很老了,但是,我最近需要查看这个答案,所以对于其他寻找此问题的人:(我使用的是VS 2012 .net 4.5,所以不确定旧版本)< / p>

我创建了一个抽象类,并在两个子类上使用了override:

public abstract class Person
{
    public string Name { get; protected set; }
    public abstract void GiveName(string inName);
}

public class Employee : Person
{
    public override void GiveName(string inName)
    {
        Name = inName;
    }
}

public class SalesPerson:Employee
{
    public override void GiveName(string inName)
    {
        Name = "Sales: "+inName;
    }
}

测试:

SalesPerson x = new SalesPerson();
x.GiveName("Mark"); //Name="Sales: Mark"
Employee e = x;
e.GiveName("Mark"); //Name="Sales: Mark"
Employee m = new Employee();
m.GiveName("Mark"); //Name="Mark"

答案 13 :(得分:-1)

我非常希望我的回答可以帮助一些人对此问题感到困惑。

请耐心等待,但我会尝试重新总结被问到的内容,以确保我回答正确的问题。然后我会给出答案!

我认为问题的实质是:如何在抽象类中声明一个方法,以便有一个实现,但仍然需要一个派生类来覆盖该方法?

C#似乎不支持此功能。如果您声明方法&#39; abstract&#39;,则不允许具有实现(正文)。但是如果你宣布它为“虚拟”,则不会强制派生类覆盖它。 C#deosn不允许将方法标记为抽象和虚拟。

我相信在很多情况下你希望这样做(被问到的是什么)。

解决这个问题的方法如下:声明两种方法!其中一个标记为“抽象”;另一个被标记为虚拟&#39;并在其身体的某个地方调用第一个。

这样,派生类被强制覆盖抽象方法,但虚拟方法中的(部分)实现可以安全地继承。

因此,例如,可以声明Employee方法GiveBonus:

public abstract decimal ComputeBonus();
public virtual void GiveBonus() { 
    decimal amount = ComputeBonus();
    if (amount > 0.0) PostBonus(amount);
}

我将PostBonus的细节留给想象,但这种方法的优点在于派生类被强制覆盖ComputeBonus,但GiveBonus受益于基类中提供的部分实现。 HTH

答案 14 :(得分:-1)

科特林

没有抽象类的唯一方法是使用 interface

interface SomeInterface {
    fun thisMethodWillBeForcedToOverride(param: Any)
}

与抽象类一起更好

//  Use this class
abstract class AbstractClass: CustomInterface {
   fun method1() {}
   fun method2() {}
}

//  
interface CustomInterface {
  fun thisMethodWillBeForcedToOverride(param: Any)
}