【问题标题】:Why isn't the more derived extension method called? [duplicate]为什么不调用更派生的扩展方法? [复制]
【发布时间】:2020-10-10 22:29:30
【问题描述】:

在下面的代码中,我(显然是错误地)期望绑定/调用更具体(如果您愿意,可以派生)类型的方法:

using System;

public class Program
{   
    public static int integer = 52;
    public static Program program = new Program();
        
    public static void Main()
    {
        TestReturn(program); // this works as expected all the way
        TestReturn(integer); // 1. this not quite...
    }
    
    public static T TestReturn<T>(T t) // 2. TestReturn<Int32> all good...
    {
        Console.WriteLine("In TestReturn<" + typeof(T) + ">");
        return (T)Extensions.Undefined(t); // 3. wrong call
    }
}

public static class Extensions
{
    public static object Undefined(this object t) // 4. this is called, instead of (5)
    {
        Console.WriteLine("In Undefined(obj)");
        return null;
    }
    
    public static int Undefined(this int b) // 5. this is expected to be called
    {
        Console.WriteLine("In Undefined(int)");
        return int.MinValue;
    }
}

输出:

In TestReturn<Program>
In Undefined(obj)
In TestReturn<System.Int32>
In Undefined(obj)
Run-time exception (line 17): Object reference not set to an instance of an object.  

有人能说出为什么会发生这种情况以及如何做到这一点才能按我的预期工作吗?

【问题讨论】:

  • 由于您对泛型类型没有任何限制,因此编译器必须选择一种适用于所有可能T的方法
  • 我想值得指出的是,如果 C# 是一种解释型语言,它可能会按预期工作,但正如 UnholySheep 指出的那样,在编译时编译器需要选择应该由 @ 调用的扩展方法987654325@,没有where 子句将默认为最通用的。
  • dotnetfiddle.net/UojDTv 会将调用哪个方法的决定推迟到运行时。这似乎是您想要的。
  • 这是我的收获:编译器在编译时无法解析T。即使代码具有具体类型的调用。它最多只能对T 做出一个大致的了解:是否 T 可以根据约束从任何调用的类型中分配。

标签: c# generics extension-methods


【解决方案1】:

取自C# Programming guide

扩展方法的基本定义说:

编译器生成的中间语言 (IL) 将您的代码转换为对静态方法的调用。

如何在运行时绑定扩展方法:

永远不会调用与接口或类方法具有相同名称和签名的扩展方法。在编译时,扩展方法的优先级总是低于类型本身定义的实例方法。

当编译器遇到方法调用时,它首先在类型的实例方法中查找匹配项。如果未找到匹配项,它将搜索为该类型定义的任何扩展方法,并绑定到它找到的第一个扩展方法。

使用结构类型扩展预定义类型可能很困难,因为它们是按值传递给方法的。这意味着对结构的任何更改都是对结构的副本进行的。扩展方法退出后,这些更改将不可见。

根据文档,@UnholySheep 和 @Alexander Høst 在 cmets 中也提到,在您的 Program 类的编译时,编译器选择更通用的类型来满足您的“T”类型的一般方法。换句话说,覆盖“对象”的扩展方法是由编译器选择的,因为“T”可能是任何东西。

以下代码无法编译的概念证明:

namespace ConsoleForEverything
{
    public class Program
    {
        public static int integer = 52;    
        public static Program program = new Program();

        public static void Main()
        {
            TestReturn(integer); // 1. this not quite...
        }

        public static T TestReturn<T>(T t) // 2. TestReturn<Int32> all good...
        {
            Console.WriteLine("In TestReturn<" + typeof(T) + ">");
            return (T)Extensions.Undefined(t); // 3. wrong call
        }
    }

    public static class Extensions
    {
        public static int Undefined(this int b) // 5. this is expected to be called
        {
            Console.WriteLine("In Undefined(int)");
            return int.MinValue;
        }
    }
}

【讨论】:

  • and how to do it in order to work as I intended? 请参阅我上面的评论。
  • 在官方文档中,引用的第 3 段很有趣,特别是“绑定到它找到的第一个扩展方法”。因此,我将在object 参数一上方编写int 参数方法:)“编译器选择了覆盖“对象”的扩展方法,因为“T”可能是任何东西”这实际上解释了一切。跨度>
  • When the compiler encounters a method invocation, it first looks for a match in the type's instance methods. If no match is found, it will search for any extension methods that are defined for the type, and bind to the first extension method that it finds. 所有这些多态性的东西只发生在编译时。对于运行时,即dynamic,如果找不到与类型完全匹配的方法,则会引发错误。
  • 是的,我同意,作为示例提供here,但是我们必须注意,在提供的示例中,我们不是直接调用您的 TestReturn 方法,而是调用扩展方法 在此方法内部,这意味着在运行时 IL 根据调用 Extensions.Undefined() 的类型做出决定。
猜你喜欢
  • 2023-04-09
  • 2011-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-16
  • 2016-12-24
相关资源
最近更新 更多