在C#中将lambda函数作为命名参数传递 - c#

编译这个简单的程序:

class Program
{
    static void Foo( Action bar )
    {
        bar();
    }

    static void Main( string[] args )
    {
        Foo( () => Console.WriteLine( "42" ) );
    }
}

那里没什么奇怪的。如果我们在lambda函数主体中出错:

Foo( () => Console.LineWrite( "42" ) );

编译器返回错误消息:

error CS0117: 'System.Console' does not contain a definition for 'LineWrite'

到目前为止,一切都很好。现在,让我们在对Foo的调用中使用命名参数:

Foo( bar: () => Console.LineWrite( "42" ) );

这次,编译器消息有些混乱:

error CS1502: The best overloaded method match for 
              'CA.Program.Foo(System.Action)' has some invalid arguments 
error CS1503: Argument 1: cannot convert from 'lambda expression' to 'System.Action'

这是怎么回事?为什么不报告实际错误?

请注意,如果我们使用匿名方法而不是lambda,则会获得正确的错误消息:

Foo( bar: delegate { Console.LineWrite( "42" ); } );

参考方案

为什么不报告实际错误?

不,这是问题所在;它正在报告实际错误。

让我用一个稍微复杂的例子来解释。假设您有:

class CustomerCollection
{
    public IEnumerable<R> Select<R>(Func<Customer, R> projection) {...}
}
....
customers.Select( (Customer c)=>c.FristNmae );

OK,根据C#规范,错误是什么?您必须在这里非常仔细地阅读规格。让我们来解决。

我们调用了Select作为具有单个参数而没有类型参数的函数调用。我们在CustomerCollection中的Select上进行查找,搜索名为Select的可调用事物-即诸如委托类型的字段或方法之类的事物。由于没有指定类型参数,因此可以在任何通用方法Select上进行匹配。我们找到一个并从中建立一个方法组。方法组包含一个元素。
现在必须通过重载解析对方法组进行分析,以首先确定候选集,然后从中确定适用的候选集,从中确定最佳的适用候选者,再从中确定最终经过验证的最佳适用的候选者。如果这些操作中的任何一个失败,则重载解析必须失败并显示错误。其中哪一个失败了?
我们从构建候选集开始。为了获得候选人,我们必须执行方法类型推断以确定类型参数R的值。方法类型推断如何工作?
我们有一个lambda,其参数类型都是已知的-正式参数是Customer。为了确定R,我们必须将lambda的返回类型映射到R。lambda的返回类型是什么?
我们假定c为客户,并尝试分析lambda主体。这样做会在客户的上下文中查找FristNmae,但查找失败。
因此,lambda返回类型推断失败,并且没有将边界添加到R。
在分析完所有参数之后,R上没有界限。因此,方法类型推断无法确定R的类型。
因此,方法类型推断失败。
因此,没有方法添加到候选集。
因此,候选集为空。
因此,将没有适用的候选人。
因此,此处的正确错误消息将类似于“过载解析由于候选集为空而无法找到最终验证的最佳适用候选者”。

客户对该错误消息将非常不满意。我们在错误报告算法中建立了许多启发式方法,试图推论出更多的“基本”错误,用户可以实际采取措施解决错误。我们的理由:

实际错误是候选集为空。为什么候选人被设置为空?
由于方法组中只有一种方法,因此类型推断失败。

确定,我们是否应该报告错误“由于方法类型推断失败而导致重载解析失败”?同样,客户对此会不满意。相反,我们再次提出问题“为什么方法类型推断失败?”

因为R的绑定集为空。

这也是一个糟糕的错误。为什么界限设置为空?

因为我们可以确定R的唯一参数是无法推断其返回类型的lambda。

好的,我们是否应该报告错误“由于lambda返回类型推断无法推断返回类型而导致过载解析失败”?同样,客户对此会不满意。取而代之的是,我们问一个问题:“为什么lambda无法推断出返回类型?”

因为客户没有名为FristNmae的成员。

那就是我们实际报告的错误。

因此,您看到了推理过程中绝对曲折的环节,以便提供所需的错误消息。我们不能只是说出出了什么问题-给过载解决方案提供了一个空的候选集-我们必须追溯到过去以确定过载解决方案如何进入该状态。

这样做的代码非常复杂。它处理的情况比我刚才介绍的要复杂得多,其中包括以下情况:存在多种不同的通用方法,并且由于多种原因导致类型推断失败,我们必须从所有这些方法中找出给出的“最佳”理由是什么。用户。回想一下,实际上有十多种不同的选择和重载解析,它们可能由于不同的原因或相同的原因而失败。

编译器的错误报告中提供了启发式方法,用于处理各种重载解决方案失败;我描述的只是其中之一。

现在,让我们看看您的特殊情况。真正的错误是什么?

我们有一个方法组,其中只有一个方法Foo。我们可以建立一个候选集吗?
是。有一个候选人。 Foo方法是调用的候选方法,因为它提供了每个必需的参数-bar-并且没有其他参数。
好的,候选集只有一个方法。候选集中有合适的成员吗?
否。因为lambda主体包含错误,所以与bar对应的参数无法转换为形式参数类型。
因此,适用的候选集为空,因此没有最终验证的最佳适用候选,因此过载解析失败。

那么错误应该是什么呢?同样,我们不能仅仅说“过载解决方案未能找到最终经过验证的最佳适用候选者”,因为客户会讨厌我们。我们必须开始挖掘错误消息。为什么重载解析失败?

因为适用的候选集为空。

为什么它是空的?

因为其中的每个候选人都被拒绝了。

有没有可能的最佳人选?

是的,只有一名候选人。

为什么被拒绝?

因为它的参数不能转换为形式参数类型。

好吧,在这一点上,显然,处理涉及命名实参的重载解决问题的启发式方法决定了我们已经深入研究了,这就是我们应该报告的错误。如果我们没有命名参数,那么其他一些启发式的问:

为什么论点不可转换?

因为lambda主体包含错误。

然后,我们报告该错误。

错误启发式方法并不完美;离得很远。碰巧的是,我本周正在对“简单的”超载解决方案错误报告启发式方法进行大量的体系结构设计-就像何时说“没有采用2个参数的方法”以及何时说“想要的方法是私有的”这样的东西”和何时说“没有对应于该名称的参数”,依此类推;完全有可能您正在调用带有两个参数的方法,不存在具有两个参数的同名公共方法,有一个私有的方法,但其中一个具有不匹配的命名参数。快速,我们应该报告什么错误?我们必须做出最好的猜测,有时我们可以做出更好的猜测,但不够复杂。

事实证明,即使正确地做到这一点也非常棘手。当我们最终重新构造大型的重型启发式方法时(例如如何处理LINQ表达式内部方法类型推断的失败),我将重新审视您的情况,看看是否可以改进启发式方法。

但是由于您得到的错误消息是完全正确的,因此这不是编译器中的错误;相反,这仅仅是在特定情况下错误报告启发式方法的缺点。

在C#.Net中找到工作项目目录 - c#

我用过Directory.getCurrentDirectory();它只给C:\ Windows \ SysWOW64但是我需要获取Visual Studio默认项目文件夹,我该如何获取?例如:C:\ Users \ knallasi \ Documents \ Visual Studio 2010 \ Projects \ SampleApp我们如何在项…

在C#中,基本构造函数调用中的一种用法如何解决多个枚举警告? - c#

以下代码向我警告了IEnumerable可能的多个枚举:public ClassName(IEnumerable<OtherClassName> models) : base(models) { _models.AddRange(models); } 由于“基本”调用,消除此警告的常规解决方案不起作用。我无法转换为列表,因为没有地方可以存储该列表…

字段初始化程序访问“this”:在C#中无效,在Java中有效? - c#

Improve this question 一,简介:这段代码:class C { int i = 5; byte[] s = new byte[i]; } 无法编译,并出现以下错误: 字段初始值设定项不能引用非静态字段,方法或属性“C.i”Resharper说类似的话:无法在静态上下文中访问非静态字段i。这与C# spec says一致-字段初始值设定项无…

在C++之后学习C# - c#

随着语言的发展,我一直在学习C和C++。现在,我想学习C#。我知道它们之间有一些巨大的差异-例如删除指针和垃圾回收。但是,我不知道两者之间的许多差异。C++程序员在转移到C#时需要知道的主要区别是什么? (例如,我可以使用什么代替STL,它们之间的语法差异或任何其他可能被认为重要的东西。) 参考方案 C# for C++ Developers是一个不错的起点…

在C#中调用C++库 - c#

Improve this question 我有很多用C++编写的库。我想从C#中调用这些库,但是遇到了很多问题。我想知道是否有一本书或指南可以告诉我如何做。 参考方案 DllImport-http://msdn.microsoft.com/en-us/library/aa984739(VS.71).aspx 包装器类-http://social.msdn.…