如何在没有抽象基类的情况下强制重写子类中的方法?

问题标题似乎有点混乱,但我会尽量澄清我的问题。

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 是一个抽象类,它有一个抽象方法 GiveBonus
  2. SalesPerson 是从 Employee 派生出来的。 SalesPerson 必须给出抽象 Method GiveBonus 的定义(销售人员不能是抽象的)
  3. PTSalesPerson 是从 SalesPerson 派生的。

现在我的问题是,我如何能让 力量 PTSalesPerson 拥有自己的 GiveBonus 实现。

107541 次浏览

除非将 SalesPerson 抽象化或更改层次结构,否则无法执行此操作。

这个怎么样:

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

Employee 和 SalesPersonBase 现在都标记为摘要。

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

注意,只有当您只考虑编译时检查时,上面的文本才是有效的。换句话说,如果您希望编译器在没有向 PTSalesPerson 类添加重写的情况下发出抱怨,那么您就不能这样做,除非您执行我上面概述的操作。

但是,没有什么可以阻止您在运行时使用反射来检查方法,并且如果 PTSalesPerson 中的方法没有在运行时被显式重写,那么就会抛出异常,不过我认为这是一种破解。

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

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

如果这个方法中有一些逻辑是所有 SalesPeople 共有的,那么您可以将 SalesPerson 上的 GiveBonus 实现为 模板法。它调用任何子类中所需的一些抽象方法:

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

将类 Employee声明为抽象类,将类 SalesPerson声明为具体类(非抽象类) ,并提供一个 GiveBonus()的实现,该实现为所提供的代码不应涵盖的任何类型或情况抛出一个运行时异常(如 “必须由子类实现”)消息。这是一个老的 Smalltalk 实践。我在 Java 代码中使用过它。

if (GetType() != typeof(SalesPerson))
{
throw new NotImplementedException("Please override in subclasses");
}


// ...proceed with giving SalesPerson a bonus

如果你不能让 SalesPerson 抽象化,我认为唯一的办法就是:

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

这里的缺点是,反射很慢。如果没有声明 GiveBonusDerived,则没有编译时错误,等等。

我觉得你想错了。语言设计者们并没有对自己说: “我们真正需要的是一种将方法标记为 必须被推翻的方法,让我们发明一种叫做 摘要的东西吧。”。他们说“一个虚拟的方法让我们表示 此基类型的每个派生类型都应该能够执行此方法。但是如果有一个 没有明智的准则可以放到这个方法的基类版本中,那该怎么办呢?我知道,让我们为这种情况发明一种叫做抽象方法的东西。”

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

C # 语言没有解决“我必须强迫我的子类型提供他们自己的方法实现”这个问题的机制,因为据我所知,语言设计人员从来没有考虑过这个问题,这对于我们的大多数客户来说是个问题。

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

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

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

但更一般地说: 我不确定您的层次结构是否在一开始就设计得合理。当我在 Employee 上看到 GiveBonus 方法时,我假设这意味着“员工可以发放奖金”,而不是“员工可以获得奖金”。当然,一个 经理给一个奖金和员工 收到奖金。我觉得你可能让员工阶层做得太多了。

使用依赖注入创建一个 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的实例(或者在 GiveBonus方法中获取 NullReferenceException)。

作为额外的额外好处,这种方法允许 Person的不同子类共享一个奖金计算方法(如果适当的话)。

剪辑

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

您总是可以让 SalesPerson 的实现抛出一个 NotExecmentedException. : V 但是根据合同,您不能这样做。

我认为这表明您的实现实际上有两部分: 一部分实现放在基类中,另一部分实现缺少在子类中需要的实现的完成。例如:

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

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

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

基类方法

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

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

好吧,我知道这个帖子很老了,但是,我最近需要查找这个答案,所以对于其他人寻找这个: (我使用 VS 2012。Net 4.5,所以不确定旧版本)

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

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"

这是一个非常古老的线程,但在 这个问题上给出的答案可能是有用的。 可以强制派生类拥有自己的虚方法/属性实现。

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

如果一个虚方法被声明为抽象的,那么它对于任何 继承自抽象类的 抽象方法无法访问 方法ー在前面的示例中,类 F 上的 DoWork 不能调用 DoWork 通过这种方式,抽象类可以强制派生类 为虚方法提供新的方法实现。

我非常希望我的答案能帮助一些对这个问题感到困惑的人。

请耐心等待,但我会试着重新总结一下被问到的问题,只是为了确保我回答的问题是正确的。那我就给你答案!

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

C # 似乎不支持这一点。如果声明该方法为“抽象”,则不允许该方法具有实现(主体)。但是如果声明它为“虚拟”,则派生类不必重写它。C # deos 不允许将一个方法同时标记为抽象的和虚拟的。

我相信有许多情况下你希望这样做(什么是被要求)。

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

这样,派生类被迫重写抽象方法,但是可以安全地继承虚方法中的(部分)实现。

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

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

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