我正在开发一个项目,我有一个通用的抽象类型,它接受一个类型参数,它本身是从抽象类型派生的。如果您想知道我为什么要这样做,请参阅this question。
我遇到了一个有趣的问题,即在抽象类中定义的派生类中重载方法。这是一个代码示例:
public abstract class AbstractConverter<T, U>
where U : AbstractConvertible
where T : AbstractConverter<T, U>
{
public abstract T Convert(U convertible);
}
public class DerivedConvertibleConverter : AbstractConverter<DerivedConvertibleConverter, DerivedConvertible>
{
public DerivedConvertibleConverter(DerivedConvertible convertible)
{
Convert(convertible);
}
public override DerivedConvertibleConverter Convert(DerivedConvertible convertible)
{
//This will not be called
System.Console.WriteLine("Called the most derived method");
return this;
}
public DerivedConvertibleConverter Convert(Convertible convertible)
{
System.Console.WriteLine("Called the least derived method");
return this;
}
}
public abstract class AbstractConvertible {}
public class Convertible : AbstractConvertible {}
public class DerivedConvertible : Convertible {}
在上面的示例中,调用了抽象父级中不存在的Convert的重载(并且派生次数较少)。我希望可以调用父类中派生最多的版本。
在尝试解决此问题时,我遇到了一个有趣的解决方案:
public abstract class AbstractConverter<U>
where U : AbstractConvertible
{
public abstract AbstractConverter<U> Convert(U convertible);
}
public class DerivedConvertibleConverter : AbstractConverter<DerivedConvertible>
{
public DerivedConvertibleConverter(DerivedConvertible convertible)
{
Convert(convertible);
}
public override DerivedConvertibleConverter Convert(DerivedConvertible convertible)
{
System.Console.WriteLine("Called the most derived method");
return this;
}
public DerivedConvertibleConverter Convert(Convertible convertible)
{
System.Console.WriteLine("Called the least derived method");
return this;
}
}
public abstract class AbstractConvertible {}
public class Convertible : AbstractConvertible {}
public class DerivedConvertible : Convertible {}
从基类中删除派生类型参数时,将调用Convert的派生类型最多的版本。我不希望这种差异,因为我不希望转换的抽象版本的接口发生变化。但是,我一定是错的。谁能解释为什么会出现这种差异?非常感谢你提前。
答案 0 :(得分:9)
在上面的示例中,调用了抽象父级中不存在的Convert的重载(并且派生次数较少)。我希望来自父类的最派生版本将被称为
很多人都有这种期望。但是,您观察到的行为是正确的并且是设计的。
重载决策算法是这样的。首先,我们列出了您可以调用的所有可访问方法。 覆盖虚方法的方法被认为是声明它们的类的方法,而不是覆盖它们的类。然后我们过滤掉那些参数无法转换为形式参数类型的方法。然后我们过滤掉任何类型 less derived 的所有方法,而不是任何具有适用方法的类型。然后我们确定哪种方法比另一种更好,如果还剩下多个方法的话。
在您的情况下,有两种可能的适用方法。采用DerivedConvertible的方法被认为是基类的方法,因此不如采用Convertible的方法。
这里的原则是覆盖虚方法是一个可能发生变化的实现细节,而不是提示编译器选择覆盖方法。
更一般地说,重载决策算法的这些功能旨在帮助缓解脆性基类问题的各种版本。
有关这些设计决策的更多详细信息,请参阅我关于此主题的文章:
http://blogs.msdn.com/b/ericlippert/archive/2007/09/04/future-breaking-changes-part-three.aspx
从基类中删除派生类型参数时,将调用Convert的派生类型最多的版本。我不希望这种差异,因为我不希望转换的抽象版本的界面发生变化
这个问题是基于错误的前提;最派生的版本是不调用。程序片段是错误的,因此不能编译,因此不会调用任何方法;程序没有运行,因为它没有编译。