为什么会调用这个被覆盖的方法?

3
public interface ITimeable     {}
public class TimedDoor : ITimeable  {}

public static class Timer
{
    public static void Add(ITimeable obj)
    {
       Console.Write("Add with parameter - ITimeable"); 
    }

    public static void Add(TimedDoor obj)
    {
       Console.Write("Add with parameter - TimedDoor"); 
    }
}

public class BaseClient<T> where T : ITimeable
{
    public T TimedDoorObject;
    public virtual void Init()
    {
        Timer.Add(TimedDoorObject);
    }
}

public class Client : BaseClient<TimedDoor>
{
    public Client()
    {
        TimedDoorObject = new TimedDoor();
    }

    public override void Init()
    {
        Timer.Add(TimedDoorObject);
    }
}

在这里,Client.Init() 返回 "带参数添加 - 带计时门"

但是如果客户端没有重写 Init(),

public class Client : BaseClient<TimedDoor>
{
    public Client()
    {
        TimedDoor = new TimedDoor();
    }
}

在这里,Client.Init()返回"带参数添加-ITimeable"
这是怎么发生的?在运行时,TimedDoorObject在两种情况下都是相同的。

1
在C#中,仅通过方法体,编译器执行重载解析; 在C#中,编译器不知道您将使用哪种类型来构建泛型。 C#泛型是通用的,它们不是像C ++中的模板那样,在每次构建时从头分析模板代码的新副本。 - Eric Lippert
1
看起来您在描述 Init 方法在两种情况下返回的内容时,交换了字符串。 - Jeppe Stig Nielsen
2个回答

8

如果我们在调用Timer.Add(TimedDoorObject)时添加一些明确表示T代表什么的显式转换,那么就更容易理解正在发生的事情。

public class BaseClient<T> where T : ITimeable
{
    public T TimedDoorObject;
    public virtual void Init()
    {
        Timer.Add((ITimeable)TimedDoorObject);
    }
}

public class Client : BaseClient<TimedDoor>
{
    public Client()
    {
        TimedDoorObject = new TimedDoor();
    }

    public override void Init()
    {
        Timer.Add((TimedDoor)TimedDoorObject);
    }
}

因此,当 BaseClient 编译时,它只知道 T 是一种 ITimeable 对象,因此它能够链接到的最佳重载是 void Add(ITimeable obj) 版本。相比之下,在编译时,Client 知道 T 表示一个 TimedDoor,因此它使用 void Add(TimedDoor obj) 函数,因为它比 void Add(ITimeable obj) 更匹配。


4

TimedDoorObject在运行时两种情况相同。

是的,但是所选方法是基于调用时参数的类型而不是当前指向的对象的类型。因此,例如,即使tdTimedDoor,这也将调用ITimeable方法:

TimeDoor td = new TimedDoor();
Timer.Add((ITimeable)td);

在基类的上下文中,TimedDoorObject字段被定义为ITimeable类型。重写的Init引用了派生类的TimedDoorObject字段,该字段被定义为TimedDoor类型。


1
正确。如果他想要不同的行为,其中实际运行时类型很重要,而不是编译时类型,他可以在虚拟方法内使用 Timer.Add((dynamic)TimedDoorObject);。当然这会更慢,因为重载解析逻辑将必须在运行时运行。 - Jeppe Stig Nielsen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接