• lambda表达式和表达式树(深入理解c#)


    1.Lambda形式

    1). Lambda表达式最冗长的形式:

    (显式类型的参数列表)=>{语句}

    2). 大多数时候,都可以用一个表达式来表示主体,该表达式的值是Lambda的结果,在这些情况下,可以指定那个表达式,不使用大括号,不使用return语句,也不添加分号。

    (显式类型的参数列表)=> 表达式

    3). 编译器大多时候都能猜出参数类型,不需要你显式声明他们(隐式类型的参数列表就是一个以逗号分隔的名称列表,没有类型,但隐式和显式类型的参数不能混合匹配——要么全是隐式的,要么全是显式的)

      (隐式类型的参数列表) => 表达式

    4). 如果Lambda表达式只需一个参数,而且那个参数可以隐式指定类型,c#3允许省略圆括号。

    参数名 => 表达式

    2. 高阶函数

    Lambda表达式的主体本身可以包含另一个Lambda表达式,但做起来就像听起来一样,很容易让人混淆,另外,Lambda表达式的参数可以是另一个委托,这样做同样很乱。

    3. 表达式树

    .Net3.5的表达式树提供了一种抽象方式将一些代码表示成一个对象树,c#3对于将Lambda表达式转换成表达式树提供了内建的支持。顾名思义,它们是对象构成的树,树中的每个节点本身就是一个表达式,不同的表达式类型代表能在代码中执行的不同操作。

    System.Linq。Expressions命名空间包含了代表表达式的各个类,它们都继承自Expession,一个抽象的主要包含一些静态工厂方法的类,这些方法用于创建其他表达式类的实例。然而Expression也包括两个属性

    1)Type属性代表表达式求值后的.Net类型,可把他视为一个返回了类型,例如,如果一个表达式要获取一个字符串的Length属性,该表达式的类型就是int。

    2)NodeType属性返回所代表的表达式的种类。他是ExpessionType枚举的成员,包括LessThan,Multiply和Invoke等。仍然使用上面的例子,对于myString.Length这个属性访问来说,其节点类型是MemberAccess。该属性最重要的地方,是它能区分由相同的类表示不同种类的表达式。

    4. 将表达式树编译成委托

    LambdaExpression是从Expression派生的类型。泛型类Expression<TDelegate>是从LambdaExpression派生的,其中泛型参数TDelegate必须是委托类型。

    LambdaExpression有个Compile方法能创建恰当类型的一个委托。而Expression<TDelegate>的Compile方法返回TDelegate类型的委托。来看看下面的例子:

    复制代码
    Expression<Func<int, int, int>> expr = (x, y) => x + y;
    
    ParameterExpression pex1 = Expression.Parameter(typeof(int), "x");//第一个参数
    ParameterExpression pex2 = Expression.Parameter(typeof(int), "y");//第二个参数
    
    BinaryExpression bexp = Expression.Add(pex1, pex2);//主体,加法
    
    //使用Expression.Lambda方法,创建一个委托类型已知的Expression
    Expression<Func<int,int,int>> lambdaExp 
        = Expression.Lambda<Func<int, int, int>>(bexp, new ParameterExpression[] { pex1, pex2 });
    
    Func<int,int,int> tDelegate = lambdaExp.Compile();//编译成委托
    
    Console.WriteLine(tDelegate(1, 3));
    
    Console.Read();
    复制代码

    我们运行上面代码,结果为:4。我们写了一大堆代码,本质上就是用表达式树计算了1+3的结果。

    5. 将c#Lambda表达式转换成表达式树

    可以要求编译器通过你的Lambda表达式构建一个表达式树,在执行时创建Expression<TDelegate>的一个实例。

    Expression<Func<int>> return5 = () => 5

    限制:

    并非所有Lambda表达式都能转换成表达式树。不能将带有一个语句块(即使只有一个return语句)的Lambda转换成表达式树——只对单个表达式进行求值的Lambda表达式才可以。表达式中还不能包含赋值操作,因为在表达式树中表示不了这种操作。尽管.Net4扩展了表达式树的功能,但只能转换单一表达式这一限制仍然有效。(上述只是最常见的,还有很多)

    代码:

    MethodInfo method = typeof(string).GetMethod("StartsWith", new[] {typeof(string) });

    var target = Expression.Parameter( typeof(string) , "x");

    var methodArg = Expression.Parameter( typeof(string) , "y");

    Expression[] methodArgs = new[] {methodArg};

    Expression call = Expression.Call( target,method,methodArgs);

    var lambdaParamethers = new[] { target,methodArg};

    var lambda = Expression.Lambda<Func<string,string,bool>>(call,lambdaParameters);

    var compiled = lambda.Compile();

    Console.WriteLine( compiled("First","Second" ));

    Console.WriteLine( compiled("First","Fir" ));

    Lambda表达式提供了编译时检查的能力,而表达式树可以将执行模型从你所需的逻辑中提取出来,提供了远程执行代码的能力。

    在讨论动态类型时,将看到更多关于动态语言运行时的内容。表达式树是其架构的核心部分,他们具有三个特点对DIR特别有吸引力:

    1)他们是不易变的,因此可以安全地储存;

    2)他们是可组合的,因此可以在简单的块中构建出复杂的行为;

    3)他们可以编译为委托,后者可以像平常那样进一步JIT编译为本地代码。

    DLR需要对如何处理不同的表达式做出决定,这些表达式会因不同的规则而发生变化。表达式树允许将这些规则(和结果)转换为代码,这与你知道所有的规则和结果后,手工编写代码非常接近。这一概念异常强大,可以使动态代码以惊人的速度执行。

  • 相关阅读:
    1988-B. 有序集合
    1987-A. 集训队选拔
    1964-NP
    1963-带妹子去看电影
    1962-Fibonacci
    1961-计算机基础知识大赛 2 (new)
    TCP/IP协议详解 卷一:协议 18章、TCP连接的建立与终止
    3、剑指offer--从尾到头打印链表
    2、剑指offer--替换空格
    1、剑指offer--二维数组中查找
  • 原文地址:https://www.cnblogs.com/mcyushao/p/9765559.html
Copyright © 2020-2023  润新知