【问题标题】:C# Method Overload Problem With Class Derived From Generic Abstract Class泛型抽象类派生类的 C# 方法重载问题
【发布时间】:2011-04-06 20:59:40
【问题描述】:

我正在做一个项目,我有一个通用抽象类型,它接受一个类型参数,该类型参数本身是从抽象类型派生的。如果您想知道我为什么要这样做,请参阅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。我没想到会有这种差异,因为我没想到 Convert 的抽象版本的接口会发生变化。但是,我一定是错的。谁能解释为什么会出现这种差异?非常感谢您。

【问题讨论】:

  • 您的第二个代码 sn-p 不应该编译顺便说一句,因为 Convert 方法不是协变的。 (即,它应该返回一个AbstractConverter&lt;DerivedConvertible&gt;,而不是更多派生的DerivedConvertibleConverted
  • 谢谢你,马克。你是对的。但是,在编译之前,IDE 会在该状态下解析正确版本的方法。如果我将方法修复为具有正确的返回类型AbstractConverter&lt;DerivedConvertible&gt;,则会出现同样的问题,导致调用派生较少的方法。也许这个问题没有变通方法。
  • 有一个简单的解决方法,也就是说(例如在第一个 sn-p 中)(this as AbstractConverter&lt;DerivedConvertibleConverter, DerivedConvertible&gt;).Convert(convertible)); 在 .ctor 中。主要问题是两个 Convert 方法都被调用匹配。我不确定编译器用于最佳匹配的标准,但似乎第二种方法更好匹配,因为 DerivedConvertible 不直接从 AbstractConvertible 继承。它唯一的直接基类是Convertible,与第二种方法完全匹配。
  • 当代码不正确时,IDE 会做出最好的猜测,它可以给出错误的程序。当你给它一个非法程序时,你不能从IDE的猜测中推断出什么是合法。它是错误恢复启发式方法,而不是展望未来,看你将如何解决错误!
  • 在这种特殊情况下,IDE 可能会猜测“覆盖”是错误,并假设您将删除它,因为该方法实际上并未覆盖任何内容;它不能,因为返回类型不匹配。如果 IDE 假定它不是覆盖,那么重载解析可能会选择它。

标签: c# overloading generics


【解决方案1】:

在上面的示例中,调用了抽象父级中不存在(并且派生较少)的 Convert 的重载。我希望从父类中调用最多的派生版本

很多人都有这样的期望。但是,您观察到的行为是正确的,并且是设计使然。

重载解决算法是这样的。首先,我们列出您可以调用的所有可能的可访问方法。 覆盖虚方法的方法被认为是声明它们的类的方法,而不是覆盖它们的类。 然后我们过滤掉那些不能将参数转换为形参类型的方法。然后,我们过滤掉所有在任何类型上派生度低于的方法,而不是具有适用方法的任何类型。然后我们确定哪种方法比另一种更好,如果还有不止一种方法。

在您的情况下,有两种可能的适用方法。采用 DerivedConvertible 的方法被认为是基类的方法,因此不如采用 Convertible 的方法好。

这里的原则是覆盖一个虚方法是一个可能发生变化的实现细节,而不是提示编译器选择覆盖方法。

更一般地说,重载解析算法的这些功能旨在帮助缓解各种版本的脆性基类问题。

有关这些设计决策的更多详细信息,请参阅我关于该主题的文章:

http://blogs.msdn.com/b/ericlippert/archive/2007/09/04/future-breaking-changes-part-three.aspx

从基类中删除派生类型参数时,将调用最派生版本的 Convert。我没想到会有这种差异,因为我没想到 Convert 的抽象版本的接口会发生变化

这个问题是基于一个错误的前提;最衍生的版本是 not 调用的。程序片段错误,因此无法编译,因此没有调用任何方法;程序没有运行,因为它没有编译。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-19
    • 2012-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-19
    • 2011-02-01
    • 1970-01-01
    相关资源
    最近更新 更多