`
RednaxelaFX
  • 浏览: 3049612 次
  • 性别: Icon_minigender_1
  • 来自: 海外
社区版块
存档分类
最新评论

LINQ与DLR的Expression tree(1):简介LINQ与Expression tree

阅读更多
(Disclaimer:如果需要转载请先与我联系;文中图片请不要直接链接
作者:RednaxelaFX at rednaxelafx.iteye.com)

系列文章:
LINQ与DLR的Expression tree(1): 简介LINQ与Expression tree
LINQ与DLR的Expression tree(2): 简介DLR
LINQ与DLR的Expression tree(3): LINQ与DLR及另外两个库的AST对比
LINQ与DLR的Expression tree(4): 创建静态类型的LINQ表达式树节点
LINQ与DLR的Expression tree(5): 用lambda表达式表示常见控制结构

以前在这边也发过关于LINQ的帖,不过并没有涉及比较深入的部分。之前的这篇:使用lambda expression来动态获取delegate,然后用Cecil注入代码(1)并不是没有下文了,而是在短短的时间内情况发生了许多变化,使得一些原本的想法有机会以更简单的方式来完成。接下来的一段时间我会发一些与LINQ、DLR等相关的帖,探讨它们与动态代码生成、动态类型代码等的关系,也将会继续讨论通过它们来进行依赖注入的话题。

关于这个“树”的叫法……LINQ里的表达式树有官方文档就叫Expression tree,但DLR中相应的语法树却没有什么固定的正式叫法,有时候叫做DLR AST,有时候叫DLR tree。本文(以及接下来的文章)都将使用DLR tree来称呼DLR中的语法树。

LINQ与Expression tree

那么,先简单的说明LINQ与Expression tree的关系。LINQ(Language-INtegrated Query)是.NET Framework 3.5新引入的技术,使得对数据集合的操作能够以统一的API实现。其中,.NET Framework自带的LINQ实现有LINQ-to-Objects、LINQ-to-DataSet、LINQ-to-SQL、LINQ-to-XML等实现。它依赖于.NET Framework 3.5引入的另外几项技术,包括扩展方法(extension method)、lambda表达式(lambda expression)、匿名类型(anonymous type)、自动实现的属性(auto-implemented property)、局部变量类型推导(local variable type inference)等。表达式树(Expression tree)则是LINQ本身的重要组成部分之一。

引用中文MSDN上的文档
引用
语言集成查询 (LINQ)
LINQ 中的表达式目录树
在 LINQ 中,表达式目录树用于表示针对数据源的结构化查询,这些数据源实现 IQueryable<T>。例如,LINQ to SQL 提供程序实现 IQueryable<T> 接口,用于查询关系数据存储。C# 和 Visual Basic 编译器会将针对此类数据源的查询编译为代码,该代码在运行时将生成一个表达式目录树。然后,查询提供程序可以遍历表达式目录树数据结构,并将其转换为适合于数据源的查询语言。

表达式目录树还可以用在 LINQ 中,用于表示分配给类型为 Expression<TDelegate> 的变量的 Lambda 表达式。

表达式目录树还可用于创建动态 LINQ 查询。有关更多信息,请参见如何:使用表达式目录树来生成动态查询。如果要生成 LINQ 提供程序,您也可以使用表达式目录树。有关更多信息,请参见演练:创建 IQueryable LINQ 提供程序

(啊哈,原来微软官方的文档是把Expression tree翻译为“表达式目录树”的啊,以前还一直不知道呢。总之我还是继续用英文标识避免歧义。)

Expression tree、委托与lambda表达式

委托(delegate)与lambda表达式

了解过C# 3.0的新特性的话应该知道,在C# 3.0中新引入了一个语法结构,称为lambda expression(lambda表达式/匿名函数)。对此尚不了解的也可以到MSDN上看看,Lambda表达式。Lambda表达式既可以赋值给一个委托(delegate)类型,例如Action、Func等系列的内建委托类型;也可以赋值给Expression<TDelegate>类型,例如以下lambda表达式:
x => -x

当它被直接赋值给Func<int, int>类型的变量时,C#编译器会将它的内容编译为一个静态方法,并创建一个对应类型的引用赋值给变量。也就是说,对于
static class Program {
    static void Main( string[ ] args ) {
        Func<int, int> negateFunc = x => -x;
    }
}

C#编译器会编译为类似下面的代码:
internal static class Program
    [CompilerGenerated]
    private static int <Main>b__0( int x ) {
        return -x;
    }

    private static void Main( string[ ] args ) {
        Func<int, int> negateFunc = new Func<int, int>( <Main>b__0 );
    }
}

(实际上还涉及到缓存那个委托,这里省略掉了。另外,之所以会编译为一个静态方法是因为这个lambda表达式没有使用任何“自由变量”,也就是既不是参数或局部变量也不是类的成员的变量。在现有的C#编译器实现中,如果一个匿名函数使用了“this”,那么对应生成的方法会是成员方法;如果使用了其它自由变量的话则会生成一个私有内部类来存放匿名函数所使用到的自由变量,并在这个内部类里生成匿名函数对应的方法。这里作为例子选择了最简单的情况来介绍。)

如此将一个lambda表达式编译为一个实际的函数后,其中的MSIL字节码可以为CLR所理解并执行。这样就足够实现in-memory query了,例如LINQ-to-Objects、LINQ-to-DataSet等。但其它平台无法理解MSIL,要对函数进行分析然后执行就会十分困难。例如说,如果想让一个lambda表达式在SQL Server上执行,该如何让SQL Server也理解它呢?

Expression tree与lambda表达式

MSIL之所以不便于分析是因为它将原本是树状结构的程序代码转换为了线性结构,损失了一些信息,主要是损失了程序代码的“结构性”,更接近于底层而降低了抽象程度。
我们知道,程序源代码对应着具体语法树(concrete syntax tree),每个叶节点对应着代码里的一个词素,其上则是各种语法结构,如表达式、语句、声明、定义等。抽象语法树(abstract syntax tree,AST)则在具体语法树的基础上将一些诸如关键字、括号等冗余信息去掉,让树更加整洁,便于分析而不损失任何有用的信息。

前面那个简单的lambda表达式,其对应的具体语法树如下图:

Expression到Unary Expression中间的虚线表示中间省略了许多层。从Expression到最后的id,中间包括expression -> non-assignment-expression -> conditional-expression -> null-coalescing-expression -> conditional-or-expression -> conditional-and-expression -> inclusive-or-expression -> exclusive-or-expression -> and-expression -> equality-expression -> relational-expression -> additive-expression -> multiplicative-expression -> unary-expression -> - unary-expression -> primary-expression -> primary-no-array-creation-expression -> simple-name -> identifier。要是画到图上太乱了……
由于具体语法树忠实的对应着原本的语法定义中的各种语法结构,这棵具体语法树中不但含有用于标识lambda表达式的箭头(“=>”),用于标识取反的一元表达式的负号(“-”),还有一堆中间为了表示表达式优先级而设置的层次(也就是省略掉的部分)。这样的语法树上有着太多的冗余信息,原本很简单的代码却变成了复杂的语法树,反而不便于分析和使用了。

既然知道这个表达式一定是一个lambda表达式,那个箭头就可以去掉;得知方法体是一个取反的一元表达式之后,负号也可以去掉;用于表示中间层次的表达式层次也全部可以去掉。将冗余信息抽取掉之后,可以得到一个等价的抽象语法树(AST):

(这幅图的解释在这里有更新)
很好,这样简单多了,而且实际有用的语法结构都保留了下来。这个AST实际上就与LINQ Expression tree的结构对应上了:
LambdaExpression
    UnaryExpression (body; negation)
        ParameterExpression ("x")
    ParameterExpression (parameter; "x")


换言之,回到代码上,如果像下面这段代码把lambda表达式“x => -x”赋值给一个Expression<Func<int,int>>类型的变量:
using System;
using System.Linq.Expressions;

static class Program {
    static void Main( string[ ] args ) {
        Expression<Func<int, int>> negateExpr = x => -x;

        Func<int, int> negateFunc = negateExpr.Compile( );

        Console.WriteLine( negateFunc( 1 ) );
    }
}

编译器会检测到赋值目标是Expression<TDelegate>,于是并不直接生成其对应的静态方法的MSIL,而是生成创建Expression tree的代码。在C#编译器的帮助下,上面的代码完全等价于以下版本:
using System;
using System.Linq.Expressions;

static class Program {
    static void Main( string[ ] args ) {
        ParameterExpression param = Expression.Parameter(typeof(int), "x");
        Expression<Func<int, int>> negateExpr =
            Expression.Lambda<Func<int, int>>(
                Expression.Negate( param ),
                new ParameterExpression[ ] { param } );

        Func<int, int> negateFunc = negateExpr.Compile( );

        Console.WriteLine( negateFunc( 1 ) );
    }
}

留意到第二个版本中是如何手工创建Expression tree的,只有这一步不同而已;将expression tree编译为委托类型,然后调用委托的部分是完全一样的。

LINQ的Expression<TDelegate>与其对应的TDelegate委托类型之间的关系是:
1、同一个lambda表达式既可以赋值给Expression<TDelegate>类型的变量也可以赋值给TDelegate类型的委托变量;
2、一个Expression<TDelegate>可以通过调用其Compile<TDelegate>()转换为一个TDelegate类型的委托(反之则不行)。

LINQ的IQueryable<T>与Expression tree

上面介绍了Expression tree与委托的关系,但是乍一看这跟LINQ的联系却不明显。
需要知道,在C# 3/VB 9里的查询表达式实际上会被翻译为针对IEnumerable<T>或者IQueryable<T>的扩展方法;这些扩展方法接收的参数就包括了Func<>系列的委托和Expression<TDelegate>。

举个简单的例子,这个C# 3的查询表达式:
from s in new [ ] { "a", "b", "cd", "efg" }
where s.Length == 1
orderby s descending
select "--" + s + "--"

会被翻译为:
( new [ ] { "a", "b", "cd", "efg" } )
    .Where( s => s.Length == 1 )
    .OrderByDescending( s => s )
    .Select( s => "--" + s + "--" )

.NET Framework中的数组由Array类实现,而它实现了IEnumerable<T>接口,所以可以直接通过Enumerable提供的扩展方法用于LINQ查询。上面的例子就是一个典型的LINQ-to-Objects的应用。
针对IEnumerable<T>的LINQ扩展方法大都接受Func<>系的委托作为参数,例如上面的Where<T>( this IEnumerable<T>, Func<T, bool> ),OrderByDescending<TSource,TKey>( this IEnumerable<TSource>, Func<TSource,TKey> ),Select<TSource,TResult>( this IEnumerable<TSource>, Func<TSource,TResult> )等。于是,作为实际参数传进去的lambda表达式就被编译为了委托。
但如果把例子稍微改动一下,使用IQueryable<T>系的方法的话:(通过AsQueryable()方法可以将一个IEnumerable<T>转换为IQueryable<T>来使用)
from s in ( new [ ] { "a", "b", "cd", "efg" } ).AsQueryable( )
where s.Length == 1
orderby s descending
select "--" + s + "--"

会被翻译为:
( new [ ] { "a", "b", "cd", "efg" } ).AsQueryable( )
    .Where( s => s.Length == 1 )
    .OrderByDescending( s => s )
    .Select( s => "--" + s + "--" )

这里针对IQueryable<T>(IEnumerable<T>的一个子接口)的扩展方法却都变成接收Expression<Func<>>系的Expression tree作为参数了;对应上面的几个方法的signature,只要在Func<>外面包装一层Expression<>就是IQueryable<T>版LINQ查询用的扩展方法的signature。至于为什么要用Expression tree,前面提到过,MSIL不便于在.NET以外的平台上分析和执行,所以委托不合适;Expression tree保留了更多的结构信息,抽象层次比MSIL高,相对来说更适合这项工作。在一个LINQ-to-SQL的查询中,其IQueryProvider会负责将Expression tree优化并转换到SQL,再交给后台的数据库来执行,等获得了执行结果后将执行结果返回给调用者。有了这个途径,LINQ就能与许多平台连接起来,为.NET上的程序对多种平台的数据访问提供统一的访问方式/API。

创建Expression tree的节点

简单介绍过LINQ中的Expression tree的概念后,让我们来讨论一下这棵树里面的节点该如何创建。LINQ Expression tree中的节点一般是通过System.Linq.Expression.Expression这个类上的工厂方法来创建的。包括以下的一些方法:
(方法名:返回值)
Add                : BinaryExpression
AddChecked         : BinaryExpression
And                : BinaryExpression
AndAlso            : BinaryExpression
ArrayIndex         : BinaryExpression
ArrayLength        : UnaryExpression
Call               : MethodCallExpression
Coalesce           : BinaryExpression
Condition          : ConditionalExpression
Constant           : ConstantExpression
Convert            : UnaryExpression
ConvertChecked     : UnaryExpression
Divide             : BinaryExpression
Equal              : BinaryExpression
ExclusiveOr        : BinaryExpression
Field              : MemberExpression
GreaterThan        : BinaryExpression
GreaterThanOrEqual : BinaryExpression
Invoke             : InvocationExpression
Lambda             : Expression<TDelegate>
LeftShift          : BinaryExpression
LessThan           : BinaryExpression
LessThanOrEqual    : BinaryExpression
ListInit           : ListInitExpression
MakeBinary         : BinaryExpression
MakeMemberAccess   : MemberExpression
MakeUnary          : UnaryExpression
MemberInit         : MemberInitExpression
Modulo             : BinaryExpression
Multiply           : BinaryExpression
MultiplyChecked    : BinaryExpression
Negate             : UnaryExpression
NegateChecked      : UnaryExpression
New                : NewExpression
NewArrayBounds     : NewArrayExpression
NewArrayInit       : NewArrayExpression
Not                : UnaryExpression
NotEqual           : BinaryExpression
Or                 : BinaryExpression
OrElse             : BinaryExpression
Parameter          : ParameterExpression
Power              : BinaryExpression
Property           : MemberExpression
PropertyOrField    : MemberExpression
Quote              : UnaryExpression
RightShift         : BinaryExpression
Subtract           : BinaryExpression
SubtractChecked    : BinaryExpression
TypeAs             : UnaryExpression
TypeIs             : TypeBinaryExpression
UnaryPlus          : UnaryExpression

上述列表中每个名字其实对应着一组重载,参数不同,但返回值的类型是一样的。这些工厂方法所生成的表达式的意义应该说还是很直观的,就不特别介绍了。经常跟编译器打交道的话应该对这些名字尤其熟悉。

这些工厂方法对应的类的层次结构是:
System.Object
  System.Linq.Expressions.Expression
    System.Linq.Expressions.BinaryExpression
    System.Linq.Expressions.ConditionalExpression
    System.Linq.Expressions.ConstantExpression
    System.Linq.Expressions.InvocationExpression
    System.Linq.Expressions.LambdaExpression
      System.Linq.Expressions.Expression<TDelegate>
    System.Linq.Expressions.MemberExpression
    System.Linq.Expressions.MethodCallExpression
    System.Linq.Expressions.NewExpression
    System.Linq.Expressions.NewArrayExpression
    System.Linq.Expressions.MemberInitExpression
    System.Linq.Expressions.ListInitExpression
    System.Linq.Expressions.ParameterExpression
    System.Linq.Expressions.TypeBinaryExpression
    System.Linq.Expressions.UnaryExpression

观察可得知,LINQ Expression tree中的类只能用于表现表达式,而无法表现“更大”的语法结构,例如赋值表达式、一般语句等。这是LINQ v1的一个重要限制,也是为什么C# 3.0的规范里明确提到一个能赋值给Expressin<TDelegate>类型的lambda表达式的方法体只能是一个表达式,而不能是一个代码块——代码块无法通过LINQ v1里的Expression tree表示。同时也值得注意的是,LINQ Expression tree本质上是一棵静态类型的树——所有节点所表示的类型都能够在编译时确定。这一点在后面提到DLR时会再展开讨论。

修改Expression tree

一棵Expession tree在创建后就不可再改变。假如某个程序接收一棵Expression tree为参数,然后仅仅是用于生成别的形式的代码(例如SQL语句),那么这个不可改变性不会有什么影响。但如果一个程序想对一棵Expression tree进行修改该怎么办呢?解决方法是从来源的Expression tree复制出一棵新的树,在复制过程中根据自己的需要选择是直接复制原有节点还是创建修改了的节点。MSDN上有一个例子解释了如何实现这种需求,如何:修改表达式目录树

更详细的LINQ Expression tree的讨论留待以后再说。下一篇将简单介绍DLR的状况,以及它与LINQ Expression tree的关系。

P.S. 这篇文发了之后才发现上个月博客园的TerryLee也写了类似的介绍,在这里:打造自己的LINQ Provider(上):Expression Tree揭秘,值得一读。

===========================================================================

文中的两幅树状图是用Graphviz的dot来绘制的,代码如下:
cst.dot:
digraph ExpressionTree {

node [fontsize=12, fontcolor=blue, font=Courier, shape=box]

 // node declarations
 lambda [label="Lambda Expression"]
 
 anoFuncSig [label="Anonymous Function\nSignature"]
 arrow [label="=>"]
 anoFuncBody [label="Anonymous Function\nBody"]
 
 impFuncSig [label="Implicit Anonymous\nFunction Signature"]
 expr [label="Expression"]
 
 impParam [label="Implicit Anonymous\nFunction Parameter"]
 uexpr1 [label="Unary Expression"]
 
 neg [label="-"]
 uexpr2 [label="Unary Expression"]
 
 id [label="Identifier:\nx"]
 
 simpName [label="Simple Name:\nx"]
 
 // relations

 lambda -> anoFuncSig
 lambda -> arrow
 lambda -> anoFuncBody
 
 {rank=same; anoFuncSig arrow anoFuncBody }
 
 anoFuncSig -> impFuncSig
 anoFuncBody -> expr
 
 {rank=same; impFuncSig expr }
 
 impFuncSig -> impParam
 expr -> uexpr1 [style=dashed]

 {rank=same; impParam uexpr1 }

 impParam -> id
 uexpr1 -> neg
 uexpr1 -> uexpr2
 
 {rank=same; id neg uexpr2 }
 
 uexpr2 -> simpName
}

ast.dot:
digraph ExpressionTree {

node [fontsize=12, fontcolor=blue, font=Courier, shape=box]
edge [fontsize=10, fontcolor=purple]
 // node declarations
 lambda [label="Lambda Expression"]
 
 param [label="Parameter:\nx"]
 body [label="Unary Expression\n(Negation)"]
 
 param2 [label="Simple Name:\nx"]
 
 // relations

 lambda -> param [label="Signature"]
 lambda -> body [label="Body"]
 
 {rank=same; param body }
 
 body -> param2
 param -> param2 [label="(same node)", fontsize=8, style=dashed, dir=both]
}
10
4
分享到:
评论
5 楼 hd700 2008-09-29  
博主加油!
4 楼 RednaxelaFX 2008-09-21  
多谢关注 ^ ^

hd700 写道
想请教一下,ExpreesionTree怎么能表达过程式编程写多条语句,例如:
public int Test(int a) {
    this.Inc(ref a);
    return a;
}

现在的LINQ Expression tree没办法表达ref或者out参数,不过多语句的方法体经过一定变换是有可能表达的;变换后就不再是多语句,而变成一个表达式了。当然,有ref和out参数的方法是没办法变换的;但有副作用的方法却未必变换不了,得看情况。

我已经写了相关的内容,在这个系列文章的4和5里。问题是4和5虽然写完了,3却还没写完,所以还得等一阵子才会发出来。不介意的话请等那些部分出来再过来看看?

P.S. 下一个版本的LINQ Expression tree应该就有办法直接表达语句的概念了,所以有很多现在很麻烦或者做不到的事情等下一版本就能很轻松的做到。
3 楼 hd700 2008-09-21  
想请教一下,ExpreesionTree怎么能表达过程式编程写多条语句,例如:
public int Test(int a)
{
    this.Inc(ref a);
    return a;
}
2 楼 RednaxelaFX 2008-09-14  
这样么……嗯有道理,我虽然懒得维护多个blog,不过到那边去发似乎更有用一些
多谢建议 ^ ^
1 楼 sdhjl2000 2008-09-14  
呵呵,看来javaeye里也有.net先锋啊,不过应该发到园子里这样才有更多人研究讨论

相关推荐

    LINQ与DLR的Expression tree(4):创建静态类型的LINQ表达式树节点

    这篇博客文章“LINQ与DLR的Expression tree(4):创建静态类型的LINQ表达式树节点”深入探讨了如何构建这种数据结构,特别是关注静态类型的表达式树节点。 Expression Tree是一种表示方法调用、条件语句、算术运算...

    LINQ中文系列教程

    LINQ初体验之LINQ to Object 1 一步一步学Linq to sql(一):预备知识 4 一步一步学Linq to sql(二):DataContext与实体 9 一步一步学Linq to sql(三):增删改 15 一步一步学Linq to sql(四):查询句法 21 ...

    LINQ中文教程

    LINQ中文教程LINQ初体验之LINQ to Object 1 一步一步学Linq to sql(一):预备知识 4 一步一步学Linq to sql(二):DataContext与实体 9 一步一步学Linq to sql(三):增删改 15 一步一步学Linq to sql(四):...

    LINQ基础教程(中文版) 适合初学者

    LINQ初体验之LINQ to Object 1 一步一步学Linq to sql(一):预备知识 4 一步一步学Linq to sql(二):DataContext与实体 9 一步一步学Linq to sql(三):增删改 15 一步一步学Linq to sql(四):查询句法 21 ...

    LINQ to Objects Using C# 4.0: Using and Extending LINQ to Objects and Parallel LINQ

    Your Complete Example-Rich Guide to Using and Extending LINQ to Objects and PLINQ Using LINQ to Objects, .NET developers can write queries over object collections with the same deep functionality ...

    expression tree.pptx

    介绍了System.Linq.Expression命名空间下的Expression类以及如何执行和修改表达式树,最后介绍了表达式树用到的访问者模式。

    MSDN:101 LINQ Samples

    1. **LINQ - Join Operators**:这部分内容讲解了如何使用JOIN操作符,包括INNER JOIN、LEFT JOIN、RIGHT JOIN和FULL JOIN,它们在处理多表关联查询时非常有用,能方便地将来自不同数据源的信息合并。 2. **LINQ - ...

    Linq.Expression.Optimizer:System.Linq.Expression表达式优化器。 http:thorium.github.ioLinq.Expression.Optimizer

    5. **SQL优化**:针对与数据库交互的查询,`Linq.Expression.Optimizer`还能够对SQL语句进行优化,如消除无效的JOIN操作,简化WHERE子句等。 值得注意的是,尽管`Linq.Expression.Optimizer`提供了强大的优化功能,...

    101个linq例子程序

    1. **查询语法**:LINQ提供了两种查询语法——查询表达式(Query Expression)和方法链(Method Chain),两者都可以实现相同的功能,但写法不同。 2. **延迟执行**:LINQ查询的执行是延迟的,这意味着查询定义...

    2008.精通LINQ数据访问技术:基于C#

    LINQ(Language Integrated Query,语言集成查询)是微软公司提供的一项新技术,能够将查询功能直接引入到.NET Framework 3.5所支持的编程语言中,如C#、***等。通过LINQ,查询操作可以通过编程语言自身来传达,而...

    linq to sql 、linq to xml 、linq to object

    1. **LINQ to SQL**: LINQ to SQL 是一种数据访问技术,它允许开发者使用C#或VB.NET的查询语法直接对SQL Server数据库进行操作。通过DataContext类,开发者可以映射数据库表到.NET对象,从而实现对象关系映射(ORM...

    精通Linq数据访问技术C#_光盘

    1. **Linq简介**:Linq是一种在C#和Visual Basic.NET中集成的查询语言,它允许开发者使用相同的语法来查询各种不同类型的数据源。Linq通过提供一组扩展方法和类,使得代码更加简洁、易读,并降低了出错的可能性。 2...

    LINQ实战+LINQ高级编程 2本电子书 中文

    1. **查询表达式(Query Expression)**:LINQ引入了一种新的查询语法,类似于SQL,但完全融入到C#和VB.NET的语言中。例如: ```csharp var query = from student in students where student.Age &gt; 18 select ...

    c#中的Linq应用.rar

    1. **Linq的基本概念**: - **查询表达式(Query Expression)**:一种以关键字`from`、`where`、`select`、`group`等组成的声明式查询语法,与SQL查询类似。 - **方法链(Method Chaining)**:通过调用如`Where...

    linq开发资料集合

    可能的内容包括:LINQ的引入背景、查询表达式语法、方法语法的使用、与数据库交互的LINQ to SQL、与对象交互的LINQ to Objects以及与XML数据操作的LINQ to XML等。读者通过此书能快速上手并理解LINQ的核心功能。 **...

    java8stream源码-101_linq_examples_java8:C#的101个LINQ示例转换为Java8

    java8流源码Java ...linq1:哪里 - 简单 1 // c# public void Linq1 () { int [] numbers = { 5 , 4 , 1 , 3 , 9 , 8 , 6 , 7 , 2 , 0 }; var lowNums = from n in numbers where n &lt; 5 select n

    CoolCode.Linq

    1. **Linq(Language Integrated Query,语言集成查询)**:是.NET框架中的一种技术,允许开发人员使用类似于SQL的语法在各种数据源上执行查询,包括集合、数据库、XML等。Linq使得代码更加简洁、可读性更强,并提供...

    Linq中文版帮助文档(.chm文件)

    1. **Linq的基本概念**:了解Linq的核心思想,包括查询表达式和方法调用两种查询方式,以及它们与传统SQL查询的区别。 2. **Linq查询语法**:学习如何使用`from`、`where`、`select`、`group by`等关键字来构建查询...

    LINQ实例教程-用Linq对TreeView进行操作

    1. **LINQ基础**:理解查询表达式(query expressions)和方法链(method chaining)两种主要的LINQ语法形式。 2. **数据源操作**:学习如何从TreeView的节点数据绑定到LINQ查询,包括选择、过滤、排序和分组节点。 ...

    C#3.0 Linq基础学习与高级应用技巧教程

    1. 查询表达式:LINQ的主要特征之一是查询表达式(Query Expression),其语法类似于SQL,但运行在内存中的对象上。例如: ```csharp var query = from student in students where student.Age &gt; 18 select ...

Global site tag (gtag.js) - Google Analytics