将扩展方法组转换为具有泛型类型的委托

本文关键字:泛型类型 扩展 方法 转换 | 更新日期: 2023-09-27 18:27:12

我在IDataReader上有两个具有以下签名的扩展方法:

internal static List<T> GetList<T>(this IDataReader reader, Func<string, T> del)
internal static double? GetDoubleOrNull(this IDataReader reader, string columnName)

GetDoubleOrNull没有任何过载。

在其他地方,我可以做

Func<string, double?> del = reader.GetDoubleOrNull;
var x = reader.GetList(del);

var x = reader.GetList<double?>(reader.GetDoubleOrNull);

或者只传入一个实例方法,如

public double? blah(string s)
var x = reader.GetList(blah);

但我不会

var x = reader.GetList(reader.GetDoubleOrNull);

编译器给出错误

cannot convert from 'method group' to 'System.Func<string,double?>'

我不明白。我认为,由于GetDoubleOrNull上没有重载,因此不会有重载解析,并且它可以从方法签名推断类型参数。

真正令人困惑的部分是它在传递blah时的工作方式。

将扩展方法组转换为具有泛型类型的委托

前言:如果您想要完整的解释,请跳到编辑部分。剧透:扩展方法是一个编译器技巧,实际上比调用它们时多了一个参数

它在方法组的分辨率中。显然,C#编译器不需要花时间来判断您使用的方法是否存在重载;它总是需要一个明确的演员阵容。结账:

C#中的方法组是什么
方法推断不适用于方法组

reader.GetDoubleOrNull返回的方法组根据您尝试将其强制转换为的内容而缩小:GetDoubleOrNull可以引用任何数量的具有该名称的重载方法。必须显式强制转换。

有趣的是,由于同样的原因,您甚至不能将方法组分配给隐式类型的变量:

var x = reader.GetDoubleOrNull;

无法编译,因为它需要显式强制转换。

编辑我确信这里的混乱与扩展方法有关:

查看以下测试类别:

public static class Extensions
{
    public static List<T> GetList<T>(this IDataReader reader, Func<string, T> del)
    {
        throw new NotImplementedException();
    }
    public static double? GetDoubleOrNull(this IDataReader reader, string columnName)
    {
        throw new NotImplementedException();
    }
    public static double? blah(this string s)
    {
        throw new NotImplementedException();
    }
}

您可以成功呼叫

var x = reader.GetList(Extensions.blah);

为什么会是blah也是一个静态扩展方法,因此,根据您的证据,上面的行似乎不应该编译。更复杂的是,让我们添加这个方法:

public static List<T> GetList2<T>(this IDataReader reader, Func<IDataReader, string, T> del) 
{ 
    throw new NotImplementedException(); 
}

您现在可以拨打

x = reader.GetList2(Extensions.GetDoubleOrNull);

并且它将正确编译。什么东西?

答案如下:扩展方法实际上并没有将方法添加到对象中它们实际上是一种编译器技巧,可以让你把这些方法当作类的一部分来编程。从这里:

在代码中,使用实例方法调用扩展方法语法。但是,由编译器将代码转换为对静态方法的调用。因此,封装的原理实际上并不是违反。事实上,扩展方法不能访问私有变量在它们扩展的类型中。

所以,当你打电话给时

var x = reader.GetDoubleOrNull("myColumnName");

实际上正在编译和执行的是这样的(一个完全合法的调用,即使它是一个扩展方法):

var x = Extensions.GetDoubleOrNull(reader, "myColumnName");

因此,当您尝试使用GetDoubleOrNull作为Func<string, double?>的参数时,编译器会说"我可以将GetDoubleOrNull变成Func<IDataReader, string, double?>,因为它有两个参数,但我不知道如何将其变成Func<string, double?>"

即使您调用它,就好像它是带有一个arg的IDataReader的实例方法一样,但它并不是:它只是一个带有两个args的静态方法,C#编译器诱使您认为它是IDataReader的一部分。

GetDoubleOrNull返回一个double?GetList需要一个IDataReader和一个System.Func<string,int?>

错误信息有点误导

公开替身?blah(字符串s)

var x=读取器。GetList(等等);

布拉是这次通话的代表。在GetList(GetDoubleOrNull)中,它是获取doubleOrNull的结果。

不过问题很好。无论我是否帮助过,一定要把答案贴出来。