代码之家  ›  专栏  ›  技术社区  ›  Roatin Marth

有人能解释这个C lambda语法吗?

c#
  •  10
  • Roatin Marth  · 技术社区  · 15 年前

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

    public class Foo
    {
      public static Func<HtmlHelper, PropertyViewModel, string> 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)第一种款式的名称是什么?我意识到它使用的是兰姆达斯 = 有迹象表明我被绊倒了。我不能标记它;)

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

    7 回复  |  直到 15 年前
        1
  •  2
  •   Davy8    15 年前

    在大多数情况下,它们在功能上似乎是等效的。实际上,您可以将普通方法作为变量传递。

    但是有一些细微的差别,比如能够重新定义函数,使其成为其他的东西。如果使用反射,也可能不同,例如,它可能没有在类的方法列表中返回。(反射部分不能100%确定)

    下面显示了如何将一个方法作为变量传递,以及第二种方法如何允许重新定义如果它是普通方法就不可能实现的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;
        }
    }
    

    这让我想到…如果所有方法都是这样声明的,那么您可以 first class functions 在C…有趣…

        2
  •  7
  •   Justin    15 年前

    好的,为了清楚起见,我将把这两个写出来(并稍微修改一下方法使其简短一些)

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

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

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

    两者之间的区别 RenderMethod 渲染委托 那是 渲染方法 是一种方法,wheras 渲染委托 是委托,或者更具体地说是委托类型的字段。这意味着renderDelegate可以分配给。

    什么是代表?

    委托是一种类型。从 MSDN documentation :

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

    本质上,您可以将委托视为方法的引用/指针,但是委托指向的方法必须与委托期望的签名匹配。例如 Func<HtmlHelper, PropertyViewModel, string> 是需要具有签名的方法的委托 string MyMethod(HtmlHelper, PropertyViewModel) 因此,我们可以像这样将带有该签名的方法分配给该委托:

    RenderDelegate = RenderMethod;
    

    重要的是要注意 代表 类型(注意大写D)和 代表 关键字(小写d)。在您的示例中,您使用 Func<> 通用对象来压缩您的代码,但是它有点模糊了这里真正发生的事情。 func<htmlhelper、propertyviewmodel、string> 是从继承的类型 Delegate ,您可以使用delegate关键字来delcare等效类型:

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

    匿名方法

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

    匿名函数

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

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

    为了避免出现大量分散在周围的短方法,可以使用匿名方法在线Delcare排序方法。关于这一点,真正有用的是内联方法可以访问包含范围中声明的变量:

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

    不过,这仍然是相当多的输入,因此lambda sytax诞生了,现在您可以改为这样做:

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

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

    代表是 难以置信地 非常有用,是.NET事件系统的基石。再多读些书来整理一下:

        3
  •  5
  •   Doctor Blue    15 年前

    a)样式是使用委托的样式。以下是等效的:

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

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

        4
  •  1
  •   Ed Swangren    15 年前

    “render”是一个函数对象,它以一个htmlhelper和一个propertyviewmodel对象作为参数,并返回一个字符串。是的,它们是等价的。

    在这种情况下,为什么会有人使用lambda而不是静态函数,这超出了我的理解范围,但我不知道上下文。我将像在第二个示例中那样声明一个静态函数。也许他们认为lambda语法是“更酷”的,并且无法自拔。

        5
  •  1
  •   Chris Shaffer    15 年前

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

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

        6
  •  1
  •   Jeffrey L Whitledge    15 年前

    “A)第一种款式的名称是什么?我意识到它使用的是lambdas;=符号让我很困惑。我不能标记它;)“

    它分析如下:

    "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 == ""
    

    很抱歉。我无法抗拒。

        7
  •  1
  •   Steve Cooper    15 年前

    在这个特定的例子中,函数是等价的。但是,一般来说,lambda形式有点强大,因为它可以携带方法不能携带的信息。

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

    然后我有一个函数(递增器),我可以反复调用它;注意它如何保持变量不变。 i ,即使它不是函数的参数。这个行为——保持方法体外部声明的变量不变——被调用 闭幕 变量。因此,作为对 (a) ,此类型的函数称为 关闭。

    至于 (二) ,正如有人指出的,您可以在任意点更改渲染函数。假设您决定更改渲染的工作方式。假设您想创建渲染的定时版本。你可以这样做;

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