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

时间:2012-03-07 12:27:48

标签: c# generics delegates extension-methods internals

我在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时的工作方式。

2 个答案:

答案 0 :(得分:8)

前言:如果您想要完整的说明,请跳至编辑。 Spoiler:扩展方法是一种编译器技巧,实际上还有一个参数比你调用它们时的样子

它是方法组的解决方案。显然C#编译器没有花时间弄清楚你使用的方法是否有重载;它总是需要一个明确的演员。退房:

What is a method group in C#?
Method Inference does not work with method group

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);

它会正确编译。是什么给了什么?

以下是答案:扩展方法不会实际向对象添加方法。它们实际上是一个编译器技巧,允许您编程好像这些方法是你班级的一部分。来自here

  

在您的代码中,您使用实例方法调用扩展方法   句法。但是,由中间语言(IL)生成   编译器将您的代码转换为静态方法的调用。   因此,封装原理并非如此   侵犯。实际上,扩展方法无法访问私有变量   他们正在扩展的类型。

所以,当你打电话

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

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

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

因此,当您尝试使用GetDoubleOrNull作为Func<string, double?>的arg时,编译器将进入&#34;我可以将GetDoubleOrNull转换为Func<IDataReader, string, double?>,因为它有两个论点,但我不知道如何把它变成Func<string, double?>&#34;

即使您将其称为IDataReader的实例方法,只有一个arg,但它不是:它只是C#编译器欺骗你思考的两个args的静态方法是IDataReader的一部分。

答案 1 :(得分:0)

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

错误消息有点误导

公共双重? blah(string s)

var x = reader.GetList(blah);

blah是本次电话会议的代表。在GetList(GetDoubleOrNull)中,它是get doubleOrNull的结果。

虽然好问题。如果我帮助与否,请发布答案。