有人可以解释这个C#lambda语法吗?

我最近发现了一个静态方法,声明为:

public class Foo { public static Func Render = (a, b) => { a.RenderPartial(b); return ""; }; } 

Intellisense建议使用(例如):

 string s = Foo.Render(htmlHelper, propertyViewModel); 

那么似乎以下是等价的:

 public static string Render(HtmlHelper a, PropertyViewModel b) { a.RenderPartial(b); return ""; } 

A)第一种风格的名称是什么? 我意识到它正在使用lambdas; 这是惹我生气的=号。 我无法将其标记化;)

B)如果两个代码块等价的,使用前者比后者有什么好处?

在大多数情况下,它们似乎function相同。 实际上,您可以将常规方法作为变量传递。

但是有一些微妙的差异,比如能够将function重新定义为其他function。 如果你使用reflection,它可能也是不同的,例如,它可能不会在类的方法列表中返回。 (reflection部分不是100%肯定)

下面显示了将方法作为变量传递以及第二种方式如何允许重新定义Func,如果它是普通方法则不可能。

 class Program { static void Main(string[] args) { Console.WriteLine(GetFunc()); //Prints the ToString of a Func Console.WriteLine(Test(5)); //Prints "test" Console.WriteLine(Test2(5)); //Prints "test" Test2 = i => "something " + i; Console.WriteLine(Test2(5)); //Prints "something 5" //Test = i => "something " + i; //Would cause a compile error } public static string Test(int a) { return "test"; } public static Func Test2 = i => { return "test"; }; public static Func GetFunc() { return Test; } } 

这只是让我思考…如果所有方法都以这种方式声明,你可以在C#中拥有真正的第一类函数 ……有趣……

好的,为了清楚起见,我将再次写出两个(并稍微修改方法以使其更短)

 public static Func RenderDelegate = (a, b) => { return a.RenderPartial(b); }; public static string RenderMethod(HtmlHelper a, PropertyViewModel b) { return a.RenderPartial(b); } 

首先请注意RenderDelegate (正如S. DePouw所写),只是一种使用lambda语法编写以下内容的奇特方式:

 public static Func RenderDelegate = delegate(HtmlHelper a, PropertyViewModel b) { return a.RenderPartial(b); }; 

RenderMethodRenderDelegate之间的区别在于RenderMethod是一种方法,而RenderDelegate是一个委托,或者更具体地说是Delegate类型的字段。 这意味着可以将RenderDelegate分配给。

什么是代表?

代表是一种类型。 从MSDN文档 :

委托是一种定义方法签名的类型,可以与具有兼容签名的任何方法相关联。

基本上,您可以将委托视为方法的引用/指针,但委托指向的方法必须与委托所期望的签名相匹配。 因此,例如Func是一个委托,它需要具有签名string MyMethod(HtmlHelper, PropertyViewModel) ,因此我们能够将具有该签名的方法分配给该委托,如下所示:

 RenderDelegate = RenderMethod; 

重要的是要注意委托类型(注意大写D)和委托关键字(小写d)之间的区别。 在你的例子中,你使用Func<>generics对象来压缩你的代码,但是这里有什么模糊的东西。 Func是一个inheritance自Delegate的类型,您可以使用delegate关键字来表示等效类型:

 delegate string MyFunction; static MyFunction RenderDelegate = RenderMethod; 

匿名方法

当我们在第一个例子中分配RenderDelegate时,我们没有将RenderDelegate设置为现有的命名方法,而是我们在线声明了一个新方法。 这称为匿名方法,因为我们能够传递代码块(也使用delegate关键字声明)作为委托参数:

Lambda函数

回到原始语法 – 您的示例使用lambda语法以一种有趣的方式对一个匿名委托进行delcare。 Lambda表达式是声明在处理列表时通常可以使用的短内联方法的好方法,例如假设我们想要按名称对HtmlHelper对象列表进行排序。 这样做的方法是传递一个将两个HtmlHelper对象与列表Sort方法进行比较的Delegate,然后sort方法使用该委托来比较和排序列表中的元素:

 static int MyComparison(HtmlHelper x, HtmlHelper y) { return x.Name.CompareTo(y.Name); } static void Main() { List myList = GetList(); myList.Sort(MyComparison); } 

为了避免散布大量的短方法,可以使用匿名方法对排序方法进行在线处理。 对此更有用的是内联方法可以访问在包含范围中声明的变量:

 int myInt = 12; List myList = GetList(); myList.Sort( delegate (HtmlHelper x, HtmlHelper y) { return x.Name.CompareTo(y.Name) - myInt; }); 

然而,这仍然相当多的打字,所以lambda sytax诞生了,现在你可以这样做:

 List myList = GetList(); myList.Sort((x, y) => {return x.Name.CompareTo(y.Name)}); 

然而,以这种方式宣布“正常”方法似乎对我来说完全没有意义(并让我的眼睛流血)

代表们非常有用,并且(除其他外)是.Net活动系统的基石。 还有一些阅读清理了一点:

  • C#中的代表和活动
  • 匿名方法(MSDN文档)
  • lambda表达式简介

A)风格是使用代表的风格。 以下是等效的:

 public static Func Render = delegate(HtmlHelper a, PropertyViewModel b) { a.RenderPartial(b); return ""; }; 

B)好处是你可以将Render视为另一种方法中的变量。 然而,在这个特定的例子中,它们或多或少具有相同的好处(尽管后者更容易理解)。

“Render”是一个函数对象,它将HtmlHelper和PropertyViewModel对象作为参数并返回一个字符串。 所以是的,它们是等价的。

为什么有人会在这种情况下使用lambda而不是静态函数超出我的范围,但我不知道上下文。 我会在你的第二个例子中声明一个静态函数。 也许他们认为lambda语法“更酷”并且无法帮助自己:)。

我认为这种语法的最大优点是你可以在不扩展类的情况下重新定义方法(只需将字段设置为新方法)。

这是个好主意吗? 可能不是。 但我相信有些地方会有意义……

“A)第一种风格的名称是什么?我意识到它正在使用lambdas;它是sign使我失望的符号。我无法将其标记化;)”

它解析如下:

 "public static Func Render = (a, b) => { a.RenderPartial(b); return ""; };" class-member-declaration ::= field-declaration field-declaration ::= field-modifiers type variable-declarators ";" "public static" field-modifiers ::= field-modifiers field-modifier "public" field-modifiers ::= field-modifier field-modifier ::= "public" "static" field-modifier ::= "static" "Func" type ::= reference-type reference-type ::= delegate-type delegate-type ::= type-name type-name ::= namespace-or-type-name namespace-or-type-name ::= identifier type-argument-list "Func" identifier == "Func" "" type-argument-list ::= "<" type-arguments ">" "HtmlHelper, PropertyViewModel, string" type-arguments ::= type-arguments "," type-argument "HtmlHelper, PropertyViewModel" type-arguments ::= type-arguments "," type-argument "HtmlHelper" type-arguments ::= type-argument type-argument ::= type type ::= type-parameter type-parameter ::= identifier identifier == "HtmlHelper" "PropertyViewModel" type-argument ::= type type ::= type-parameter type-parameter ::= identifier identifier == "PropertyViewModel" "string" type-argument ::= type type ::= type-parameter type-parameter ::= identifier identifier == "string" "Render = (a, b) => { a.RenderPartial(b); return ""; }" variable-declarators ::= variable-declarator variable-declarator ::= identifier "=" variable-initializer (Here is the equals!) "Render" identifier == "Render" "(a, b) => { a.RenderPartial(b); return ""; }" variable-initializer ::= expression expression ::= non-assignment-expression non-assignment-expression ::= lambda-expression lambda-expression ::= anonymous-function-signature "=>" anonymous-function-body "(a, b)" anonymous-function-signature ::= implicit-anonymous-function-signature implicit-anonymous-function-signature ::= "(" implicit-anonymous-function-parameter-list ")" "a, b" implicit-anonymous-function-parameter-list ::= implicit-anonymous-function-parameter-list "," implicit-anonymous-function-parameter "a" implicit-anonymous-function-parameter-list ::= implicit-anonymous-function-parameter implicit-anonymous-function-parameter == identifier identifier == "a" "b" implicit-anonymous-function-parameter == identifier identifier == "b" "{ a.RenderPartial(b); return ""; }" anonymous-function-body ::= block block ::= "{" statement-list "}" "a.RenderPartial(b); return "";" statement-list ::= statement-list statement "a.RenderPartial(b);" statement-list ::= statement statement ::= embedded-statement embedded-statement ::= expression-statement expression-statement ::= statement-expression ";" "a.RenderPartial(b)" statement-expression ::= invocation-expression invocation-expression ::= primary-expression "(" argument-list ")" "a.RenderPartial" primary-expression ::= primary-no-array-creation-expression primary-no-array-creation-expression ::= member-access member-access ::= primary-expression "." identifier "a" primary-expression ::= primary-no-array-creation-expression primary-no-array-creation-expression ::= simple-name simple-name ::= identifier identifier == "a" "RenderPartial" identifier == "RenderPartial" "b" argument-list ::= argument argument ::= expression expression ::= non-assignment-expression non-assignment-expression ::= conditional-expression conditional-expression ::= null-coalescing-expression null-coalescing-expression ::= conditional-or-expresion conditional-or-expresion ::= conditional-and-expression conditional-and-expression ::= inclusive-or-expression inclusive-or-expression ::= exclusive-or-expression exclusive-or-expression ::= and-expression and-expression ::= equality-expression equality-expression ::= relational-expression relational-expression ::= shift-expression shift-expression ::= additive-expression additive-expression ::= multiplicitive-expression multiplicitive-expression ::= unary-expression unary-expression ::= primary-expression primary-expression ::= primary-no-array-creation-expression primary-no-array-creation-expression ::= simple-name simple-name ::= identifier identifer == "b" "return "";" statement ::= embedded-statement embedded-statement ::= jump-statement jump-statement ::= return-statement return-statement ::= "return" expression ";" """" expression ::= non-assignment-expression non-assignment-expression ::= conditional-expression conditional-expression ::= null-coalescing-expression null-coalescing-expression ::= conditional-or-expresion conditional-or-expresion ::= conditional-and-expression conditional-and-expression ::= inclusive-or-expression inclusive-or-expression ::= exclusive-or-expression exclusive-or-expression ::= and-expression and-expression ::= equality-expression equality-expression ::= relational-expression relational-expression ::= shift-expression shift-expression ::= additive-expression additive-expression ::= multiplicitive-expression multiplicitive-expression ::= unary-expression unary-expression ::= primary-expression primary-expression ::= primary-no-array-creation-expression primary-no-array-creation-expression ::= literal literal ::= string-literal string-literal == "" 

对于那个很抱歉。 我无法抗拒。

在这个特定的例子中,函数是等价的。 但是,一般来说,lambdaforms更强大,因为它可以携带该方法无法获得的信息。 例如,如果我这样做;

 int i = 0; Func Incrementer = () => i++; 

然后我有一个function(增量器),我可以一遍又一遍地调用; 注意它如何保持变量i ,即使它不是函数的参数。 这种行为 – 保持在方法体外声明的变量 – 被称为关闭变量。 因此,在回答(A)时 ,这种类型的函数称为闭包。

至于(B) ,正如有人指出的那样,你可以随时改变渲染function。 假设你决定改变Render的工作方式。 假设你想要创建一个定时版本的渲染。 你可以这样做;

 Foo.Render = (a, b) => { var stopWatch = System.Diagnostics.Stopwatch.StartNew(); a.RenderPartial(b); System.Diagnostics.Debug.WriteLine("Rendered " + b.ToString() + " in " + stopWatch.ElapsedMilliseconds); return ""; };