c#试图理解组合,但我的包装器对象正在访问基类字段,而不是使用派生实现

c#试图理解组合,但我的包装器对象正在访问基类字段,而不是使用派生实现,c#,C#,意式浓缩咖啡将作为成分,摩卡咖啡将作为包装。 现在,当我不使用合成进行实例化时,代码按预期执行并返回描述:Espresso: public abstract class Beverage { public string description = "Unknown Beverage"; public string GetDescription() { return description; } } public abstract class Co

意式浓缩咖啡将作为成分,摩卡咖啡将作为包装。 现在,当我不使用合成进行实例化时,代码按预期执行并返回描述:Espresso:

public abstract class Beverage
{
    public string description = "Unknown Beverage";
    public string GetDescription()
    {
        return description;
    }
}

public abstract class CondimentDecorator : Beverage
{
    public abstract new string GetDescription();
}


public class Espresso : Beverage 
{
    public Espresso()
    {
        description = "Espresso";
    }

class Mocha : CondimentDecorator
{

    Beverage beverage;

    public Mocha(Beverage beverage)
    {
        this.beverage = beverage;
    }
    public override string GetDescription()
    {
        return beverage.GetDescription() + ", Mocha";
    }
但是,当我使用composition时,将访问饮料基类中的饮料描述字段,程序将返回描述“Unknown beverage”。但是,我希望得到输出:“Espresso,Mocha”


这是因为
Beverage
中的
GetDescription
方法不是虚拟的。编写
beverage.GetDescription()
时,编译器查看并看到
beverage
的类型是
beverage
,然后看到它有一个非虚拟的
GetDescription
方法。由于它不是虚拟的,因此不会调用派生的
GetDescription

你想要的是更像这样的东西:

 Beverage beverage2 = new Espresso();

        beverage2 = new Mocha(beverage2);

        Console.WriteLine(beverage2.GetDescription());

这是因为
Beverage
中的
GetDescription
方法不是虚拟的。编写
beverage.GetDescription()
时,编译器查看并看到
beverage
的类型是
beverage
,然后看到它有一个非虚拟的
GetDescription
方法。由于它不是虚拟的,因此不会调用派生的
GetDescription

你想要的是更像这样的东西:

 Beverage beverage2 = new Espresso();

        beverage2 = new Mocha(beverage2);

        Console.WriteLine(beverage2.GetDescription());
每当一个类定义一个虚拟函数(或方法)时,大多数编译器都会向该类添加一个隐藏的成员变量,该变量指向一个指向(虚拟)函数的指针数组,称为虚拟方法表(VMT或Vtable)。这些指针在运行时用于调用适当的函数实现,因为在编译时,可能还不知道是调用基函数还是由从基类继承的类实现的派生函数。

@凯指出了问题所在。该方法需要定义为虚拟。然后运行时知道查找运行时类型的覆盖方法,在本例中为Moccha。另外,若调味品装饰师是抽象的,那个么就不需要重新定义调味品装饰师

public abstract class Beverage
{
    public string description = "Unknown Beverage";
    public virtual string GetDescription()
    {
        return description;
    }
}

public abstract class CondimentDecorator : Beverage
{
    public override string GetDescription()
    {
        return GetDescriptionOverride();
    }

    protected abstract string GetDescriptionOverride();
}
这样做,您将看到输出

public abstract class Beverage
{
    public string description = "Unknown Beverage";
    public virtual string GetDescription()
    {
        return description;
    }
}

public abstract class CondimentDecorator : Beverage
{

}

//no change to the rest
...
每当一个类定义一个虚拟函数(或方法)时,大多数编译器都会向该类添加一个隐藏的成员变量,该变量指向一个指向(虚拟)函数的指针数组,称为虚拟方法表(VMT或Vtable)。这些指针在运行时用于调用适当的函数实现,因为在编译时,可能还不知道是调用基函数还是由从基类继承的类实现的派生函数。

@凯指出了问题所在。该方法需要定义为虚拟。然后运行时知道查找运行时类型的覆盖方法,在本例中为Moccha。另外,若调味品装饰师是抽象的,那个么就不需要重新定义调味品装饰师

public abstract class Beverage
{
    public string description = "Unknown Beverage";
    public virtual string GetDescription()
    {
        return description;
    }
}

public abstract class CondimentDecorator : Beverage
{
    public override string GetDescription()
    {
        return GetDescriptionOverride();
    }

    protected abstract string GetDescriptionOverride();
}
这样做,您将看到输出

public abstract class Beverage
{
    public string description = "Unknown Beverage";
    public virtual string GetDescription()
    {
        return description;
    }
}

public abstract class CondimentDecorator : Beverage
{

}

//no change to the rest
...

在下面这行中,您隐藏了基类上的GetDescription方法

"Espresso, Mocha"
在第二个示例中,您看到了问题所在,您将变量键入为。因此,最终将在基类上调用该方法

举个简单的例子:

public abstract new string GetDescription();
然后尝试以下更改:

public class First
{
    public string GetName()
    {
        return "First";
    }
}

public class Second : First
{
    public new string GetName()
    {
        return "Second";
    }
}

public static class Program
{

    public static void Main(string[] args)
    {
        Second second = new Second();
        Console.WriteLine(second.GetName());
        // Prints "Second"

        First first = second;
        Console.WriteLine(first.GetName());
        // Prints "First"

        Console.ReadKey();
    }
}

您会发现它两次都是第二次打印。

下面一行隐藏了基类上的GetDescription方法

"Espresso, Mocha"
在第二个示例中,您看到了问题所在,您将变量键入为。因此,最终将在基类上调用该方法

举个简单的例子:

public abstract new string GetDescription();
然后尝试以下更改:

public class First
{
    public string GetName()
    {
        return "First";
    }
}

public class Second : First
{
    public new string GetName()
    {
        return "Second";
    }
}

public static class Program
{

    public static void Main(string[] args)
    {
        Second second = new Second();
        Console.WriteLine(second.GetName());
        // Prints "Second"

        First first = second;
        Console.WriteLine(first.GetName());
        // Prints "First"

        Console.ReadKey();
    }
}

你会发现它两次都是第二次打印。

为什么要在抽象方法声明中使用
new
关键字?此外,你不能重写C中的方法,除非它是
虚拟的
抽象的
Beverage.GetDescription()
两者都不是。我找不到解释得更清楚的帖子,但我认为这与您选择左侧的
Beverage
类型有关,因此调用
beverage2.GetDescription()
只调用基类方法。您可能必须强制转换它,
Console.WriteLine(((Mocha)beverage2.GetDescription())
为什么要在抽象方法声明中使用
new
关键字?此外,不能重写C中的方法,除非它是
virtual
abstract
Beverage.GetDescription()
两者都不是。我找不到解释得更清楚的帖子,但我认为这与您选择左侧的
Beverage
类型有关,因此调用
beverage2.GetDescription()
只调用基类方法。您可能必须强制转换它,
Console.WriteLine(((Mocha)beverage2.GetDescription())谢谢Karle,Kai和觉醒字节,所有答案都非常有用。你的例子特别有用,Karle,谢谢!谢谢Karle,Kai和觉醒字节,所有答案都非常有用。你的例子特别有用,Karle,谢谢!