• 不可不知的表达式树(1)Expression初探


    说起Lambda表达式,大家基本都很熟悉了,而表达式树(Expression Trees),则属于80%的工作中往往都用不到的那种技术,所以即便不是什么新技术,很多人对其理解都并不透彻。此文意图从表达式树基本技术点结合实际应用,逐步来看表达式树究竟是怎么一回事,希望能帮助读者彻底学会表达式树^_^

    一、初见表达式树Expression<TDelegate>

    不妨回想一下,你第一次使用表达式树是在哪里呢,比如Expression<TDelegate>?

    比如在linq查询中,常常用到Where方法过滤,OrderBy排序等,调用的是IQueryable<TSource>的静态扩展类Queryable的静态扩展方法。

     1 // C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\System.Core.dll
     2 namespace System.Linq
     3 {
     4     //
     5     // 摘要:提供一组用于查询实现 System.Linq.IQueryable`1 的数据结构的 static方法。
     6     //     
     7     public static class Queryable
     8     {
     9         //...
    10         public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
    11         public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector);
    12         //...
    13     }
    14 }

    可以看到里面的参数类型Expression<Func<TSource, bool>> predicate和Expression<Func<TSource, TKey>> keySelector,咦?这不就是表达式树(Expression<TDelegate>)吗?!是的,但不止如此。

    Expression<TDelegate>是表达式树的一种,并不等同于表达式树。

    二、从Expression<TDelegate>到LambdaExpression

    看下面的代码:

    1  Func<Person, bool> func1 = x => x.Age > 18;
    2  Func<Person, string> func2 = x => x.Name;
    3  Expression<Func<Person, bool>> expression1 = x => x.Age > 18;
    4  Expression<Func<Person, string>> expression2 = x => x.Name;
    5  //错误CS0834,无法将具有语句体的lambda表达式转换为表达式树
    6  Func<Person, int, int> func3 = (x, y) => { return x.Age + y; };
    7  Action<Person, int> func4 = (x, y) => { };
    8  Expression<Func<Person, int, int>> expression3 = (x, y) => { return x.Age + y; };
    9  Expression<Action<Person, int>> expression4 = (x, y) => { };

    expression1和expression2可以编译通过,expression3和expression4则编译错误,提示"无法将具有语句体的Lambda表达式转换为表达式树 "。

    这是因为,我们可以将Lambda表达式赋值给泛型表达式类型变量(Expression<TDelegate>),编译器会将我们的Lambda表达式编译为表达式树,但是仅限于expression lambdas(表达式Lambdas,也叫做single-line lambdas,即不具有语句体的Lambda表达式),相对的statement lambdas(语句Lambdas,也叫做multi-line lambdas),编译器则会报错。

    看看反编译后的func1,func2,expression1,expression2。

        ParameterExpression expression3;
        Func<Person, bool> func = x => x.Age > 0x12;
        Func<Person, string> func2 = x => x.Name;
        ParameterExpression[] parameters = new ParameterExpression[] { expression3 };
        Expression<Func<Person, bool>> expression = Expression.Lambda<Func<Person, bool>>(Expression.GreaterThan(Expression.Property(expression3 = Expression.Parameter(typeof(Person), "x"), (MethodInfo) methodof(Person.get_Age)), Expression.Constant(0x12, typeof(int))), parameters);
        ParameterExpression[] expressionArray2 = new ParameterExpression[] { expression3 };
        Expression<Func<Person, string>> expression2 = Expression.Lambda<Func<Person, string>>(Expression.Property(expression3 = Expression.Parameter(typeof(Person), "x"), (MethodInfo) methodof(Person.get_Name)), expressionArray2);
    

    expression1和expression2变成了一堆复杂的语句,这才是它们作为表达式树原有的面目^_^

    而Expression<TDelegate>来自何方?它继承自LambdaExpression。

    来看看Expression<TDelegate>反编译后的构造函数

     1 [__DynamicallyInvokable]
     2 public sealed class Expression<TDelegate> : LambdaExpression
     3 {
     4     // 构造函数
     5     internal Expression(Expression body, string name, bool tailCall, ReadOnlyCollection<ParameterExpression> parameters) : 
     6    base(typeof(TDelegate), name, body, tailCall, parameters)
     7     {
     8     }
     9     //...
    10 }

    构造函数什么也没有做,只是将参数传递给父类构造函数,所以可以认为Expression<TDelegate>是对LambdaExpression的一层封装,而LambdaExpression又是继承自Expression,是表达式树的一种,而且是最特别的一种,为何特别,且看下文^_^

    三、何为表达式树

    首先来看下面的类图,可以更直观看到Expression<TDelegate>、LambdaExpression、Expression三者的关系。

    黄线表示,使用表达式Lambdas赋值给Expression<TDelegate>类型变量,TDelegate会赋值给父类LambdaExpression的Type属性(联系下前面讲的构造函数的事情 ^_^ )

    重点看绿线,LambdaExpression继承自Expression,而LambdaExpression的Body属性,又是Expression类型。

    哇哦,眼尖的你注意到LambdaExpression还有个Compile()方法,它会编译Body,生成表示 lambda 表达式的可执行委托,然后你就可以调用委托了,所以真正的表达式树所谓的树,就是藏在Body里啦

    So,其实Expression的子类型多达几十种,例如ParameterExpression,BinaryExpression,MethodCallExpression等等,但是只有LambdaExpression(Lambda表达式树)可以执行!

    如果你还有疑问,那么任意一棵其它类型的表达式树,怎样执行呢?答案自然是创建一个新的LambdaExpression了,将表达式树作为LambdaExpression的Body,然后你懂的^_^

    具体可以调用这个方法 public static Expression<TDelegate> Lambda<TDelegate>(Expression body, params ParameterExpression[] parameters);

    此时,我们再来解答最初的疑问,究竟何为表达式树(Expression Trees)?

    表达式树即一份树形结构存储的代码,树的每个结点又都是一个表达式树,你可以编译然后运行这份树形代码。

    那么可以用它做什么呢?

    1. 表示Lambda表达式,这个显而易见了;
    2. 修改可执行代码,创建动态查询;
    3. 定制自己的IQueryable,通过翻译表达式树数据结构为特定的查询语言,实现对特定数据源的查询,即定制orm。与我们对各种数据库的Linq查询同理;

    也就是说小到根据字段字符串参数的过滤排序等,大到定制自己的orm,你都离不开表达式树。

    下篇文章中,我们将就表达式树的具体用途,来做实例展示。

    四、手动创建表达式树

    可以通过哪些途径来创建表达式树呢?通过上文我们知道可以使用表达式Lambda由编译器来创建Lambda表达式树,例如Expression<Func<int, bool>> lambda = num => num < 5;

    而另外一个更通用的方式,就是引入System.Linq.Expressions命名空间,手动构造每一个表达式树结点,来创建表达式树。

    来看几个简单的例子,一看就懂。

     1 class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             var ints = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
     6             Func1(ints);
     7             Func2(ints);
     8             Func3(ints);
     9             Console.Read();
    10         }
    11         /// <summary>
    12         /// 目标表达式树 x => x > 5 && x <= 7
    13         /// </summary>
    14         public static void Func1(int[] ints)
    15         {
    16             //创建参数x
    17             var parameter = Expression.Parameter(typeof(int), "x");
    18             //创建表达式x>5
    19             var con1 = Expression.Constant(5);
    20             var bin1 = Expression.GreaterThan(parameter, con1);
    21             //创建表达式x<=7
    22             var con2 = Expression.Constant(7);
    23             var bin2 = Expression.LessThanOrEqual(parameter, con2);
    24             //组合两个表达式
    25             var body = Expression.And(bin1, bin2);
    26             //获取Lambda表达式
    27             var lambda = Expression.Lambda<Func<int, bool>>(body, parameter);
    28             var ints2 = ints.Where(lambda.Compile());
    29             //执行结果
    30             Console.WriteLine("\r\n Func1构造的表达式树Body:\r\n {0}", body);
    31             Console.WriteLine("\r\n Func1构造的表达式树:\r\n {0}", lambda);
    32             Console.WriteLine("\r\n Func1的结果:\r\n {0}", string.Join(",", ints2));
    33         }
    34         /// <summary>
    35         /// 目标表达式树 x => x % 2 == 0 ? x : 0
    36         /// </summary>
    37         public static void Func2(int[] ints)
    38         {
    39             //创建参数 x
    40             var parameter = Expression.Parameter(typeof(int), "x");
    41             //创建表达式 x % 2
    42             var con1 = Expression.Constant(2);
    43             var bin1 = Expression.Modulo(parameter, con1);
    44             //创建表达式 (x % 2) == 0
    45             var con2 = Expression.Constant(0);
    46             var bin2 = Expression.Equal(bin1, con2);
    47             //创建表达式 x % 2 == 0 ? x : 0
    48             var body = Expression.Condition(bin2, parameter, Expression.Constant(0));
    49             //获取Lambda表达式
    50             var lambda = Expression.Lambda<Func<int, int>>(body, parameter);
    51             var ints2 = ints.Select(lambda.Compile());
    52             //执行结果
    53             Console.WriteLine("\r\n Func2构造的表达式树Body:\r\n {0}", body);
    54             Console.WriteLine("\r\n Func2构造的表达式树:\r\n {0}", lambda);
    55             Console.WriteLine("\r\n Func2的结果:\r\n {0}", string.Join(",", ints2));
    56         }
    57         /// <summary>
    58         /// 目标表达式树 x => Console.WriteLine(x)
    59         /// </summary>
    60         /// <param name="ints"></param>
    61         public static void Func3(int[] ints)
    62         {
    63             //创建参数i
    64             var parameter = Expression.Parameter(typeof(int), "x");
    65             //获取Console.WriteLine MethodInfo
    66             MethodInfo method = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) });
    67             //创建表达式
    68             var body = Expression.Call(method, parameter);
    69             var lambda = Expression.Lambda<Action<int>>(body, parameter);
    70             Console.WriteLine("\r\n Func3构造的表达式树Body:\r\n {0}", body);
    71             Console.WriteLine("\r\n Func3构造的表达式树:\r\n {0}", lambda);
    72             Array.ForEach(ints, lambda.Compile());
    73         }
    74     }
    View Code

     输出结果:

    怎么样,动手来写几个表达式树吧?!代码实战会让你更加轻松的入门表达式树技术。

    这里贴出Func1中的lambda变量监视图,看看这棵树吧^_^

    通过上面几个小例子,相信你对表达式树的手动创建已经基本了解,同时可以看到手动创建远没有使用表达式Lambda来的方便,但是却可以突破single-line lambdas限制实现更多强大的功能。

    那么Expression有多少种呢?看下图。这些都是继承自Expression,至于每种的用途,可以在VS中自行浏览,使用时根据我们的实际用途来选用不同的Expression来构建表达式树即可。

    这些表达式目录树节点的类型共有85种。

      1     //
      2     // 摘要:
      3     //     描述表达式目录树的节点的节点类型。
      4     public enum ExpressionType
      5     {
      6         //
      7         // 摘要:
      8         //     加法运算,如 a + b,针对数值操作数,不进行溢出检查。
      9         Add = 0,
     10         //
     11         // 摘要:
     12         //     加法运算,如 (a + b),针对数值操作数,进行溢出检查。
     13         AddChecked = 1,
     14         //
     15         // 摘要:
     16         //     按位或逻辑 AND 运算,如 C# 中的 (a & b) 和 Visual Basic 中的 (a And b)。
     17         And = 2,
     18         //
     19         // 摘要:
     20         //     条件 AND 运算,它仅在第一个操作数的计算结果为 true 时才计算第二个操作数。它与 C# 中的 (a && b) 和 Visual Basic 中的
     21         //     (a AndAlso b) 对应。
     22         AndAlso = 3,
     23         //
     24         // 摘要:
     25         //     获取一维数组长度的运算,如 array.Length。
     26         ArrayLength = 4,
     27         //
     28         // 摘要:
     29         //     一维数组中的索引运算,如 C# 中的 array[index] 或 Visual Basic 中的 array(index)。
     30         ArrayIndex = 5,
     31         //
     32         // 摘要:
     33         //     方法调用,如在 obj.sampleMethod() 表达式中。
     34         Call = 6,
     35         //
     36         // 摘要:
     37         //     表示 null 合并运算的节点,如 C# 中的 (a ?? b) 或 Visual Basic 中的 If(a, b)。
     38         Coalesce = 7,
     39         //
     40         // 摘要:
     41         //     条件运算,如 C# 中的 a > b ? a : b 或 Visual Basic 中的 If(a > b, a, b)。
     42         Conditional = 8,
     43         //
     44         // 摘要:
     45         //     一个常量值。
     46         Constant = 9,
     47         //
     48         // 摘要:
     49         //     强制转换或转换运算,如 C#中的 (SampleType)obj 或 Visual Basic 中的 CType(obj, SampleType)。对于数值转换,如果转换后的值对于目标类型来说太大,这不会引发异常。
     50         Convert = 10,
     51         //
     52         // 摘要:
     53         //     强制转换或转换运算,如 C#中的 (SampleType)obj 或 Visual Basic 中的 CType(obj, SampleType)。对于数值转换,如果转换后的值与目标类型大小不符,则引发异常。
     54         ConvertChecked = 11,
     55         //
     56         // 摘要:
     57         //     除法运算,如 (a / b),针对数值操作数。
     58         Divide = 12,
     59         //
     60         // 摘要:
     61         //     表示相等比较的节点,如 C# 中的 (a == b) 或 Visual Basic 中的 (a = b)。
     62         Equal = 13,
     63         //
     64         // 摘要:
     65         //     按位或逻辑 XOR 运算,如 C# 中的 (a ^ b) 或 Visual Basic 中的 (a Xor b)。
     66         ExclusiveOr = 14,
     67         //
     68         // 摘要:
     69         //     “大于”比较,如 (a > b)。
     70         GreaterThan = 15,
     71         //
     72         // 摘要:
     73         //     “大于或等于”比较,如 (a >= b)。
     74         GreaterThanOrEqual = 16,
     75         //
     76         // 摘要:
     77         //     调用委托或 lambda 表达式的运算,如 sampleDelegate.Invoke()。
     78         Invoke = 17,
     79         //
     80         // 摘要:
     81         //     lambda 表达式,如 C# 中的 a => a + a 或 Visual Basic 中的 Function(a) a + a。
     82         Lambda = 18,
     83         //
     84         // 摘要:
     85         //     按位左移运算,如 (a << b)。
     86         LeftShift = 19,
     87         //
     88         // 摘要:
     89         //     “小于”比较,如 (a < b)。
     90         LessThan = 20,
     91         //
     92         // 摘要:
     93         //     “小于或等于”比较,如 (a <= b)。
     94         LessThanOrEqual = 21,
     95         //
     96         // 摘要:
     97         //     创建新的 System.Collections.IEnumerable 对象并从元素列表中初始化该对象的运算,如 C# 中的 new List<SampleType>(){
     98         //     a, b, c } 或 Visual Basic 中的 Dim sampleList = { a, b, c }。
     99         ListInit = 22,
    100         //
    101         // 摘要:
    102         //     从字段或属性进行读取的运算,如 obj.SampleProperty。
    103         MemberAccess = 23,
    104         //
    105         // 摘要:
    106         //     创建新的对象并初始化其一个或多个成员的运算,如 C# 中的 new Point { X = 1, Y = 2 } 或 Visual Basic 中的 New
    107         //     Point With {.X = 1, .Y = 2}。
    108         MemberInit = 24,
    109         //
    110         // 摘要:
    111         //     算术余数运算,如 C# 中的 (a % b) 或 Visual Basic 中的 (a Mod b)。
    112         Modulo = 25,
    113         //
    114         // 摘要:
    115         //     乘法运算,如 (a * b),针对数值操作数,不进行溢出检查。
    116         Multiply = 26,
    117         //
    118         // 摘要:
    119         //     乘法运算,如 (a * b),针对数值操作数,进行溢出检查。
    120         MultiplyChecked = 27,
    121         //
    122         // 摘要:
    123         //     算术求反运算,如 (-a)。不应就地修改 a 对象。
    124         Negate = 28,
    125         //
    126         // 摘要:
    127         //     一元加法运算,如 (+a)。预定义的一元加法运算的结果是操作数的值,但用户定义的实现可以产生特殊结果。
    128         UnaryPlus = 29,
    129         //
    130         // 摘要:
    131         //     算术求反运算,如 (-a),进行溢出检查。不应就地修改 a 对象。
    132         NegateChecked = 30,
    133         //
    134         // 摘要:
    135         //     调用构造函数创建新对象的运算,如 new SampleType()。
    136         New = 31,
    137         //
    138         // 摘要:
    139         //     创建新的一维数组并从元素列表中初始化该数组的运算,如 C# 中的 new SampleType[]{a, b, c} 或 Visual Basic 中的
    140         //     New SampleType(){a, b, c}。
    141         NewArrayInit = 32,
    142         //
    143         // 摘要:
    144         //     创建新数组(其中每个维度的界限均已指定)的运算,如 C# 中的 new SampleType[dim1, dim2] 或 Visual Basic 中的
    145         //     New SampleType(dim1, dim2)。
    146         NewArrayBounds = 33,
    147         //
    148         // 摘要:
    149         //     按位求补运算或逻辑求反运算。在 C# 中,它与整型的 (~a) 和布尔值的 (!a) 等效。在 Visual Basic 中,它与 (Not a) 等效。不应就地修改
    150         //     a 对象。
    151         Not = 34,
    152         //
    153         // 摘要:
    154         //     不相等比较,如 C# 中的 (a != b) 或 Visual Basic 中的 (a <> b)。
    155         NotEqual = 35,
    156         //
    157         // 摘要:
    158         //     按位或逻辑 OR 运算,如 C# 中的 (a | b) 或 Visual Basic 中的 (a Or b)。
    159         Or = 36,
    160         //
    161         // 摘要:
    162         //     短路条件 OR 运算,如 C# 中的 (a || b) 或 Visual Basic 中的 (a OrElse b)。
    163         OrElse = 37,
    164         //
    165         // 摘要:
    166         //     对在表达式上下文中定义的参数或变量的引用。有关详细信息,请参阅System.Linq.Expressions.ParameterExpression。
    167         Parameter = 38,
    168         //
    169         // 摘要:
    170         //     对某个数字进行幂运算的数学运算,如 Visual Basic 中的 (a ^ b)。
    171         Power = 39,
    172         //
    173         // 摘要:
    174         //     具有类型为 System.Linq.Expressions.Expression 的常量值的表达式。System.Linq.Expressions.ExpressionType.Quote
    175         //     节点可包含对参数的引用,这些参数在该节点表示的表达式的上下文中定义。
    176         Quote = 40,
    177         //
    178         // 摘要:
    179         //     按位右移运算,如 (a >> b)。
    180         RightShift = 41,
    181         //
    182         // 摘要:
    183         //     减法运算,如 (a - b),针对数值操作数,不进行溢出检查。
    184         Subtract = 42,
    185         //
    186         // 摘要:
    187         //     算术减法运算,如 (a - b),针对数值操作数,进行溢出检查。
    188         SubtractChecked = 43,
    189         //
    190         // 摘要:
    191         //     显式引用或装箱转换,其中如果转换失败则提供 null,如 C# 中的 (obj as SampleType) 或 Visual Basic 中的 TryCast(obj,
    192         //     SampleType)。
    193         TypeAs = 44,
    194         //
    195         // 摘要:
    196         //     类型测试,如 C# 中的 obj is SampleType 或 Visual Basic 中的 TypeOf obj is SampleType。
    197         TypeIs = 45,
    198         //
    199         // 摘要:
    200         //     赋值运算,如 (a = b)。
    201         Assign = 46,
    202         //
    203         // 摘要:
    204         //     表达式块。
    205         Block = 47,
    206         //
    207         // 摘要:
    208         //     调试信息。
    209         DebugInfo = 48,
    210         //
    211         // 摘要:
    212         //     一元递减运算,如 C# 和 Visual Basic 中的 (a - 1)。不应就地修改 a 对象。
    213         Decrement = 49,
    214         //
    215         // 摘要:
    216         //     动态操作。
    217         Dynamic = 50,
    218         //
    219         // 摘要:
    220         //     默认值。
    221         Default = 51,
    222         //
    223         // 摘要:
    224         //     扩展表达式。
    225         Extension = 52,
    226         //
    227         // 摘要:
    228         //     “跳转”表达式,如 C# 中的 goto Label 或 Visual Basic 中的 GoTo Label。
    229         Goto = 53,
    230         //
    231         // 摘要:
    232         //     一元递增运算,如 C# 和 Visual Basic 中的 (a + 1)。不应就地修改 a 对象。
    233         Increment = 54,
    234         //
    235         // 摘要:
    236         //     索引运算或访问使用参数的属性的运算。
    237         Index = 55,
    238         //
    239         // 摘要:
    240         //     标签。
    241         Label = 56,
    242         //
    243         // 摘要:
    244         //     运行时变量的列表。有关详细信息,请参阅System.Linq.Expressions.RuntimeVariablesExpression。
    245         RuntimeVariables = 57,
    246         //
    247         // 摘要:
    248         //     循环,如 for 或 while。
    249         Loop = 58,
    250         //
    251         // 摘要:
    252         //     多分支选择运算,如 C# 中的 switch 或 Visual Basic 中的 Select Case。
    253         Switch = 59,
    254         //
    255         // 摘要:
    256         //     引发异常的运算,如 throw new Exception()。
    257         Throw = 60,
    258         //
    259         // 摘要:
    260         //     try-catch 表达式。
    261         Try = 61,
    262         //
    263         // 摘要:
    264         //     取消装箱值类型运算,如 MSIL 中的 unbox 和 unbox.any 指令。
    265         Unbox = 62,
    266         //
    267         // 摘要:
    268         //     加法复合赋值运算,如 (a += b),针对数值操作数,不进行溢出检查。
    269         AddAssign = 63,
    270         //
    271         // 摘要:
    272         //     按位或逻辑 AND 复合赋值运算,如 C# 中的 (a &= b)。
    273         AndAssign = 64,
    274         //
    275         // 摘要:
    276         //     除法复合赋值运算,如 (a /= b),针对数值操作数。
    277         DivideAssign = 65,
    278         //
    279         // 摘要:
    280         //     按位或逻辑 XOR 复合赋值运算,如 C# 中的 (a ^= b)。
    281         ExclusiveOrAssign = 66,
    282         //
    283         // 摘要:
    284         //     按位左移复合赋值运算,如 (a <<= b)。
    285         LeftShiftAssign = 67,
    286         //
    287         // 摘要:
    288         //     算术余数复合赋值运算,如 C# 中的 (a %= b)。
    289         ModuloAssign = 68,
    290         //
    291         // 摘要:
    292         //     乘法复合赋值运算,如 (a *= b),针对数值操作数,不进行溢出检查。
    293         MultiplyAssign = 69,
    294         //
    295         // 摘要:
    296         //     按位或逻辑 OR 复合赋值运算,如 C# 中的 (a |= b)。
    297         OrAssign = 70,
    298         //
    299         // 摘要:
    300         //     对某个数字进行幂运算的复合赋值运算,如 Visual Basic 中的 (a ^= b)。
    301         PowerAssign = 71,
    302         //
    303         // 摘要:
    304         //     按位右移复合赋值运算,如 (a >>= b)。
    305         RightShiftAssign = 72,
    306         //
    307         // 摘要:
    308         //     减法复合赋值运算,如 (a -= b),针对数值操作数,不进行溢出检查。
    309         SubtractAssign = 73,
    310         //
    311         // 摘要:
    312         //     加法复合赋值运算,如 (a += b),针对数值操作数,进行溢出检查。
    313         AddAssignChecked = 74,
    314         //
    315         // 摘要:
    316         //     乘法复合赋值运算,如 (a *= b),针对数值操作数,进行溢出检查。
    317         MultiplyAssignChecked = 75,
    318         //
    319         // 摘要:
    320         //     减法复合赋值运算,如 (a -= b),针对数值操作数,进行溢出检查。
    321         SubtractAssignChecked = 76,
    322         //
    323         // 摘要:
    324         //     一元前缀递增,如 (++a)。应就地修改 a 对象。
    325         PreIncrementAssign = 77,
    326         //
    327         // 摘要:
    328         //     一元前缀递减,如 (--a)。应就地修改 a 对象。
    329         PreDecrementAssign = 78,
    330         //
    331         // 摘要:
    332         //     一元后缀递增,如 (a++)。应就地修改 a 对象。
    333         PostIncrementAssign = 79,
    334         //
    335         // 摘要:
    336         //     一元后缀递减,如 (a--)。应就地修改 a 对象。
    337         PostDecrementAssign = 80,
    338         //
    339         // 摘要:
    340         //     确切类型测试。
    341         TypeEqual = 81,
    342         //
    343         // 摘要:
    344         //     二进制反码运算,如 C# 中的 (~a)。
    345         OnesComplement = 82,
    346         //
    347         // 摘要:
    348         //     true 条件值。
    349         IsTrue = 83,
    350         //
    351         // 摘要:
    352         //     false 条件值。
    353         IsFalse = 84
    354     }
    View Code

    --参考阅读--

    https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/

  • 相关阅读:
    排序--插入排序(Insertion Sort)Java实现
    汉诺塔--递归和非递归 Java实现
    关于mysql使用utf8编码在cmd窗口无法添加中文数据的问题以及解决 方法二
    mysql在cmd中查询到的汉字乱码问题解决 方法一
    mysql绿色版安装以及遇到的问题
    关于按下回车键自动提交表单问题解决
    获取iframe引入页面内的元素
    ORACLE 中如何截取到时间的年月日中的年、月、日
    Jboss 7配置日志
    java中将科学技术发转为正常数据
  • 原文地址:https://www.cnblogs.com/jiujiduilie/p/8963639.html
Copyright © 2020-2023  润新知