【问题标题】:Polymorphic nature of using Func<T> in C# [duplicate]在 C# 中使用 Func<T> 的多态性 [重复]
【发布时间】:2015-12-22 04:02:02
【问题描述】:

上周我遇到了一个有趣的问题,我不确定我是否真正理解following code 的多态性。我根据自己编写的一些代码创建了这个示例。

基本设置

  • 我有一个知道如何训练动物的“训练师”课程。
  • 我将它用作基类并创建了一个子类“DogTrainer”,它只知道如何训练狗。
  • 我使用超类作为返回类型创建了一个函数指针。
  • 然后我调用该函数来获取子类“DogTrainer”的新实例。
  • 然后我调用从函数指针返回的实例的“Train”方法。
  • “Train”方法调用“Trainer”-“Train”方法而不是“DogTrainer”-“Train”方法,如预期的那样

这里是代码

// Does not work as expected
// Calls the Trainer Train not the DogTrainer Train
var getTrainer = new Func<Trainer>(() => new DogTrainer(new Dog()));
var funcTrainer = getTrainer();
Console.WriteLine(funcTrainer.Train());

现在,如果我使用接口作为返回类型,它会按预期“IF”工作,我将接口直接标记在“DogTrainer”子类上

// Works as expected 
var getITrainer = new Func<ITrainer>(() => new DogTrainer(new Dog()));
var funcITrainer = getITrainer();
Console.WriteLine(funcITrainer.Train());

如果我在子类上没有接口,它将无法按预期工作。 See Example

// Does not work as expected 
// Calls the Trainer Train not the Dog Trainer Train
var getITrainerWithNoInterface = new Func<ITrainer>(() => new DogTrainerWithNoInterface(new Dog()));
var funcITrainerWithNoInterface = getITrainerWithNoInterface();
Console.WriteLine(funcITrainerWithNoInterface.Train());

如果有人可以让我知道我在这里缺少什么,那么这种行为不是我所期望的。当我在我的代码中发现这个错误时,我能够解决它,我在这里寻找的是“为什么”会发生这种情况。

这里是DogTrainerWithNoInterface,它可能是谜题的主要部分 (Trainer:ITrainer)

public class DogTrainerWithNoInterface : Trainer
{
    public DogTrainerWithNoInterface(IAnimal animal)
        : base(animal)
    { }

    public new string Train()
    {
        return $"Speak Ubu, Speak : {Animal.Speak()}";
    }
}

【问题讨论】:

  • 您看到这种行为是因为协变泛型类型,而不是因为多态性
  • 这是语言规范中所称的“接口重新实现”的行为。我在一个类似的例子中描述了它here。它与Func&lt;T&gt; 无关,这只会使这里不必要地复杂化。
  • 感谢您将问题与良好的紧凑样本放在一起。正如@mikez 指出的那样,大部分问题已经有了很好的答案(链接为重复)。第一个样本被What is shadowing覆盖。
  • 感谢您的信息。我知道现在发生了什么。

标签: c# polymorphism func


【解决方案1】:

注意:此答案纯粹是为了描述“新”问题,因为不适合评论。请忽略投票,因为它没有完全回答他的问题。

来自 OP 链接的示例:

public class DogTrainer : Trainer, ITrainer
{
    public DogTrainer(IAnimal animal)
        : base(animal)
    { }

    public new string Train()
    {
        return $"Speak Ubu, Speak : {Animal.Speak()}";
    }
}

在声明方法/属性时不要使用new。在底座上制作virtual,然后再制作override。您正在做的是将方法隐藏在您的基础上。

您只能在使用该特定类型 (DogTrainer) 时调用您的 new 方法。对基础的任何向下转换都会调用它的方法,即使用声明为 Trainer 或 ITrainer 的变量。

Knowing When to Use Override and New Keywords (C# Programming Guide)

【讨论】:

  • 请注意,您的回答与 OP 观察到的几乎相反 - 第二个版本(基于接口)显然成功调用了 DogTrainer.Train 而不是基础版本。
  • 是的。我想我应该注意到这一点。太多了,无法放入评论区。
猜你喜欢
  • 2019-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-01
  • 2010-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多