可能重复:Why my Virtual method is not overriden?
我一直在经历Head First Design Patterns。在旁注中,我开始将本书作为Code Complete 2的先决条件。无论如何,我正在使用Decorator模式(the chapter can actually even be read online)。
所以,我有4个班级:
以下是类1,3,4的源代码。
饮料类:
public abstract class Beverage
{
public Beverage()
{
Description = "Unknown Beverage";
}
public String getDescription()
{
return Description;
}
public abstract double cost();
public String Description { get; set; }
}
BeverageDecorator类:
public abstract class BeverageDecorator : Beverage
{
public new abstract String getDescription();
}
摩卡课程:
public class Mocha : BeverageDecorator
{
Beverage beverage;
public Mocha(Beverage beverage)
{
this.beverage = beverage;
}
public override string getDescription()
{
return beverage.Description + ", Mocha";
}
public override double cost()
{
return beverage.cost() + .20;
}
}
所以他们非常直截了当。然后,当我将此代码放入Main()方法时,我不断收到“Unknown Beverage”描述。
static void Main(string[] args)
{
Beverage beverage = new Espresso();
beverage = new Mocha(beverage);
Console.WriteLine(beverage.Description +
" $" + beverage.cost());
Console.Read();
}
Mocha 从它继承的类 - Beverage Decorator - 转到上面的类 - Beverage Class。即使我在Beverage Decorator中有方法,我也会覆盖它。为什么会这样?我知道它与抽象类有关,但我似乎无法弄明白。
答案 0 :(得分:5)
你没有真正地覆盖它。您已使用new
运算符隐藏其下方的成员。该运算符的一个问题是,如果调用者使用对包含稍后隐藏的成员的类型的引用,则它会绕过隐藏。在您的情况下,当您使用Beverage
并期望看到被覆盖的结果时:
Beverage beverage = new Espresso();
beverage = new Mocha(beverage);
Console.WriteLine(beverage.Description + " $" + beverage.cost());
要覆盖某些内容,需要virtual
或abstract
,并且覆盖成员需要使用override
。您的成员不是抽象的或虚拟的,在您的派生类中,您new string getDescription
不是override string getdescription
。
因此,当您与Beverage
交谈时,代码会忽略BeverageDecorator
中的隐藏。
关于装饰器模式的更大讨论,装饰类不需要继承它所装饰的东西。通常通过将装饰类作为构造参数来完成。如果你需要装饰和一些多态性,可以考虑使用一个接口:
interface ICommonStuff
{
string getDescription();
}
public class Beverage : ICommonStuff { }
public class BeverageDecorator : ICommonStuff
{
public BeverageDecorator(Beverage b) { }
}
然而,你真的需要装饰吗?这是我在节日期间经常问的事情......
答案 1 :(得分:2)
嗯,有两个问题。首先,看起来您的Description
属性应该调用getDescription
而不是相反。然后,getDescription
类需要虚拟Beverage
,BeverageDecorator
中重写而不是隐藏。
您的代码看起来像C#和Java的混合物并没有帮助。例如,你真的需要一个Description
属性和一个getDescription
方法吗?不可否认,覆盖半个属性有点棘手(至少,我总是对确切的规则感到困惑)但是在C#代码中使用camelCased名称肯定是奇怪。
这是一个替代实现(没有装饰器类 - 目前还不清楚它是如何帮助的):
using System;
public abstract class Beverage
{
public Beverage()
{
Description = "Unknown Beverage";
}
private string description;
public virtual string Description
{
get { return description; }
set { description = value; }
}
}
public class Espresso : Beverage
{
public Espresso()
{
Description = "Espresso";
}
}
public class Mocha : Beverage
{
private readonly Beverage beverage;
public Mocha(Beverage beverage)
{
this.beverage = beverage;
}
public override string Description
{
get { return beverage.Description + ", Mocha"; }
set { /* Ignored */ }
}
}
public class Test
{
static void Main()
{
Beverage espresso = new Espresso();
Beverage mocha = new Mocha(espresso);
Console.WriteLine(mocha.Description); // Prints Espresso, Mocha
}
}
答案 2 :(得分:2)
public new abstract String getDescription();
这是不方法覆盖,它是与基类中的getDescription
方法无关的新方法。
Beverage.getDescription
无法覆盖,因为它不是虚拟的。相反地声明它:
public abstract String getDescription();
或者那样:
public virtual String getDescription()
{
return Description;
}