简体   繁体   English

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

[英]Can someone explain this C# lambda syntax?

I recently came upon a static method declared as: 我最近发现了一个静态方法,声明为:

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

Intellisense suggests the usage is (for example): Intellisense建议使用(例如):

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

It would seem then that the following is equivalent: 那么看起来以下是等价的:

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

A) What is the name of the first style? A)第一种风格的名称是什么? I realize it's using lambdas; 我意识到它正在使用lambdas; it's the = sign that is tripping me up. 这是惹我生气的=号。 I can't tokenize it ;) 我无法将其标记化;)

B) If the two code blocks are equivalent, what is the benefit of using the former over the latter? B)如果两个代码块等价的,使用前者比后者有什么好处?

Ok, for clarity I'm going to write the two out again (and slightly modify the method to make it shorter) 好的,为了清楚起见,我将再次写出两个(并稍微修改方法以使其更短)

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

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

Firstly note that RenderDelegate is (as S. DePouw writes), just a fancy way of using lambda syntax to write the following: 首先请注意RenderDelegate (正如S. DePouw所写),只是一种使用lambda语法编写以下内容的奇特方式:

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

The difference between RenderMethod and RenderDelegate is that RenderMethod is a method, wheras RenderDelegate is a delegate, or more specifically a field of type Delegate. 之间的差RenderMethodRenderDelegateRenderMethod是一种方法,wheras RenderDelegate为代表,或更具体地类型代表的字段。 This means that RenderDelegate can be assigned to. 这意味着可以将RenderDelegate分配给。

What is a Delegate? 什么是代表?

A delegate is a type. 代表是一种类型。 From the MSDN documentation : MSDN文档

A delegate is a type that defines a method signature, and can be associated with any method with a compatible signature. 委托是一种定义方法签名的类型,可以与具有兼容签名的任何方法相关联。

Essentially you can think of a delegate as a reference / pointer to a method, however the method that the delegate points to has to match the signature that the delegate is expecting. 本质上,您可以将委托视为方法的引用/指针,但委托指向的方法必须与委托所期望的签名相匹配。 So for example Func<HtmlHelper, PropertyViewModel, string> is a Delegate that expects methods with the signature string MyMethod(HtmlHelper, PropertyViewModel) and so we are able to assign methods with that signature to that delegate like this: 因此,例如Func<HtmlHelper, PropertyViewModel, string>是一个委托,它需要具有签名string MyMethod(HtmlHelper, PropertyViewModel) ,因此我们能够将具有该签名的方法分配给该委托,如下所示:

RenderDelegate = RenderMethod;

Its important to note the difference between the Delegate type (note the capital D) and the delegate keyword (lower case d). 重要的是要注意委托类型(注意大写D)和委托关键字(小写d)之间的区别。 In your example your using the Func<> generic object to condense your code, however its kind of obscuring whats really going on here. 在你的例子中,你使用Func<>泛型对象来压缩你的代码,但是这里有什么模糊的东西。 Func<HtmlHelper, PropertyViewModel, string> is a type which inherits from Delegate , and you could use the delegate keyword to delcare an equivalent type: Func<HtmlHelper, PropertyViewModel, string>是一个继承自Delegate的类型,您可以使用delegate关键字来表示等效类型:

delegate string MyFunction<HtmlHelper helper, PropertyViewModel string>;
static MyFunction RenderDelegate = RenderMethod;

Anonymous methods 匿名方法

When we assigned RenderDelegate in the first example, we didnt set RenderDelegate to an existing named method, instead we declared a new method in-line. 当我们在第一个例子中分配RenderDelegate时,我们没有将RenderDelegate设置为现有的命名方法,而是我们在线声明了一个新方法。 This is known as an Anonymous Method and works because we are able to pass a code block (also declared using the delegate keyword) as a delegate parameter: 这称为匿名方法,因为我们能够传递代码块(也使用delegate关键字声明)作为委托参数:

Lambda functions Lambda函数

Back to the original syntax - your example is using lambda syntax to delcare an anonymous delegate in a funny way. 回到原始语法 - 您的示例使用lambda语法以一种有趣的方式对一个匿名委托进行delcare。 Lambda expressions are good way of declaring short inline methods which might commonly be used when dealing with lists, for example supposing we want to sort a list of HtmlHelper objects by their Name. Lambda表达式是声明在处理列表时通常可以使用的短内联方法的好方法,例如假设我们想要按名称对HtmlHelper对象列表进行排序。 The way of doing this is to pass a Delegate that compares two HtmlHelper objects to the lists Sort method, the sort method then uses that delegate to compare and sort the elements in the list: 这样做的方法是传递一个将两个HtmlHelper对象与列表Sort方法进行比较的Delegate,然后sort方法使用该委托来比较和排序列表中的元素:

static int MyComparison(HtmlHelper x, HtmlHelper y)
{
    return x.Name.CompareTo(y.Name);
}

static void Main()
{
    List<HtmlHelper> myList = GetList();
    myList.Sort(MyComparison);
}

To avoid having loads of short methods scattered around, you can use anonymous methods to delcare the sorting method in-line. 为了避免散布大量的短方法,可以使用匿名方法对排序方法进行在线处理。 Whats also really useful about this is that the in-line method has access to variables declared in the containing scope: 对于这一点而言,真正有用的是内联方法可以访问在包含范围中声明的变量:

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

Thats still fairly quite a lot of typing however, and so the lambda sytax was born and now you can do this instead: 然而,这仍然相当多的打字,所以lambda sytax诞生了,现在你可以这样做:

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

Declaring "normal" methods in this way however seems to be completely pointless to me (and makes my eyes bleed) 然而,以这种方式宣布“正常”方法似乎对我来说完全没有意义(并让我的眼睛流血)

Delegates are incredibly useful and are (among other things) the cornerstone of the .Net event system. 代表们非常有用,并且(除其他外)是.Net事件系统的基石。 Some more reading to clear things up a bit: 还有一些阅读清理一下:

A) The style is that of using delegates. A)风格是使用代表的风格。 The following is equivalent: 以下是等效的:

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

B) The benefit is that you could then treat Render as a variable within another method. B)好处是你可以将Render视为另一种方法中的变量。 In this particular instance, however, they're more or less the same benefits-wise (although the latter is a bit easier to comprehend). 然而,在这个特定的例子中,它们或多或少具有相同的好处(尽管后者更易于理解)。

For the most part they seem functionally equivalent. 在大多数情况下,它们似乎功能相同。 In fact you can pass around a normal method as a variable. 实际上,您可以将常规方法作为变量传递。

But there are subtle differences like being able to redefine the function to be something else. 但是有一些微妙的差异,比如能够将功能重新定义为其他功能。 It is probably also different if you're using reflection, eg it probably isn't returned in the list of methods on the class. 如果你使用反射,它可能也是不同的,例如它可能不会在类的方法列表中返回。 (Not 100% sure on the reflection part) (反射部分不是100%肯定)

The following shows passing a method as a variable as well as how the 2nd way allows redefining the Func that wouldn't be possible if it were a normal method. 下面显示了将方法作为变量传递以及第二种方式如何允许重新定义Func,如果它是普通方法则不可能。

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(GetFunc());   //Prints the ToString of a Func<int, string>
        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<int, string> Test2 = i =>
    {
        return "test";
    };

    public static Func<int, string> GetFunc()
    {
        return Test;
    }
}

This just got me thinking... if all methods were declared this way, you could have real first class functions in C#... Interesting.... 这只是让我思考...如果所有方法都以这种方式声明,你可以在C#中拥有真正的第一类函数 ......有趣......

"Render" is a function object which takes an HtmlHelper and a PropertyViewModel object as its argument and returns a string. “Render”是一个函数对象,它以HtmlHelper和PropertyViewModel对象作为参数并返回一个字符串。 So yes, they are equivalent. 所以是的,它们是等价的。

Why someone would use the lambda instead of a static function in this case is beyond me, but I do not know the context. 为什么有人会在这种情况下使用lambda而不是静态函数超出我的范围,但我不知道上下文。 I would just declare a static function as you have in your second example. 我会在你的第二个例子中声明一个静态函数。 Perhaps they think the lambda syntax is "cooler" and can't help themselves :). 也许他们认为lambda语法“更酷”并且无法帮助自己:)。

I think the biggest advantage to this syntax is that you can redefine the method without extending the class (just by setting the field to a new method). 我认为这种语法的最大优点是你可以在不扩展类的情况下重新定义方法(只需将字段设置为新方法)。

Is it a good idea? 这是个好主意吗? Probably not. 可能不是。 But I'm sure there are places that it would make sense... 但我确信有些地方会有意义......

"A) What is the name of the first style? I realize it's using lambdas; it's the = sign that is tripping me up. I can't tokenize it ;)" “A)第一种风格的名称是什么?我意识到它正在使用lambdas;它是sign诱惑我的标志。我无法将其标记化;)”

It parses like this: 它解析如下:

"public static Func<HtmlHelper, PropertyViewModel, string> 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<HtmlHelper, PropertyViewModel, string>"
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"

"<HtmlHelper, PropertyViewModel, string>"
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 == ""

Sorry about that. 对于那个很抱歉。 I couldn't resist. 我无法抗拒。

In this particular example, the functions are equivalent. 在这个特定的例子中,函数是等价的。 However, in general the lambda form is a bit more powerful because it can carry around information that the method can't. 但是,一般来说,lambda形式更强大,因为它可以携带该方法无法获得的信息。 For example, if I do this; 例如,如果我这样做;

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

Then I have a function (Incrementer) which I can call over and over; 然后我有一个功能(增量器),我可以一遍又一遍地调用; note how it keeps hold of the variable i , even though it's not a parameter to the function. 注意它如何保持变量i ,即使它不是函数的参数。 That behaviour -- of keeping hold of the variable declared outside the method body -- is called closing over the variable. 这种行为 - 保持在方法体外声明的变量 - 被称为关闭变量。 Consequently, and in answer to (A) , this type of function is called a closure. 因此,在回答(A)时 ,这种类型的函数称为闭包。

As to (B) , as someone has pointed out, you could change the Render function at any point. 至于(B) ,正如有人指出的那样,你可以随时改变渲染功能。 Let's say you decide to change how Render works. 假设您决定更改渲染的工作方式。 Let's say you wanted to create a timed version of render. 假设你想要创建一个定时版本的渲染。 You could do this; 你可以这样做;

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM