打造自己的LINQ Provider(上):Expression Tree揭秘

概述

在.NET Framework 3.5中提供了LINQ 支持后,LINQ就以其强大而优雅的编程方式赢得了开发人员的喜爱,而各种LINQ Provider更是满天飞,如LINQ to NHibernate、LINQ to Google等,大有“一切皆LINQ”的趋势。LINQ本身也提供了很好的扩展性,使得我们可以轻松的编写属于自己的LINQ Provider。

本文为打造自己的LINQ Provider系列文章第一篇,主要介绍表达式目录树(Expression Tree)的相关知识。

认识表达式目录树

究竟什么是表达式目录树(Expression Tree),它是一种抽象语法树或者说它是一种数据结构,通过解析表达式目录树,可以实现我们一些特定的功能(后面会说到),我们首先来看看如何构造出一个表达式目录树,最简单的方法莫过于使用Lambda表达式,看下面的代码:

Expression<Func<int, int, int>> expression = (a, b) => a * b + 2;

在我们将Lambda表达式指定给Expression<TDelegate>类型的变量(参数)时,编译器将会发出生成表达式目录树的指令,如上面这段代码中的Lambda表达式(a, b) => a * b + 2将创建一个表达式目录树,它表示的是一种数据结构,即我们把一行代码用数据结构的形式表示了出来,具体来说最终构造出来的表达式目录树形状如下图所示:

TerryLee_0160

这里每一个节点都表示一个表达式,可能是一个二元运算,也可能是一个常量或者参数等,如上图中的ParameterExpression就是一个参数表达式,ConstantExpression是一个常量表达式,BinaryExpression是一个二元表达式。我们也可以在Visual Studio中使用Expression Tree Visualizer来查看该表达式目录树:

TerryLee_0166

查看结果如下图所示:

TerryLee_0162

这里说一句,Expression Tree Visualizer可以从MSDN Code Gallery上的LINQ Sample中得到。现在我们知道了表达式目录树的组成,来看看.NET Framework到底提供了哪些表达式?如下图所示:

TerryLee_0161

它们都继承于抽象的基类Expression,而泛型的Expression<TDelegate>则继承于LambdaExpression。在Expression类中提供了大量的工厂方法,这些方法负责创建以上各种表达式对象,如调用Add()方法将创建一个表示不进行溢出检查的算术加法运算的BinaryExpression对象,调用Lambda方法将创建一个表示lambda 表达式的LambdaExpression对象,具体提供的方法大家可以查阅MSDN。上面构造表达式目录树时我们使用了Lambda表达式,现在我们看一下如何通过这些表达式对象手工构造出一个表达式目录树,如下代码所示:

static void Main(string[] args)
{
    ParameterExpression paraLeft = Expression.Parameter(typeof(int), "a");
    ParameterExpression paraRight = Expression.Parameter(typeof(int), "b");

    BinaryExpression binaryLeft = Expression.Multiply(paraLeft, paraRight);
    ConstantExpression conRight = Expression.Constant(2, typeof(int));

    BinaryExpression binaryBody = Expression.Add(binaryLeft, conRight);

    LambdaExpression lambda = 
        Expression.Lambda<Func<int, int, int>>(binaryBody, paraLeft, paraRight);

    Console.WriteLine(lambda.ToString());

    Console.Read();
}

这里构造的表达式目录树,仍然如下图所示:

TerryLee_0160

运行这段代码,看看输出了什么:

TerryLee_0158  

可以看到,通过手工构造的方式,我们确实构造出了同前面一样的Lambda表达式。对于一个表达式目录树来说,它有几个比较重要的属性:

Body:指表达式的主体部分;

Parameters:指表达式的参数;

NodeType:指表达式的节点类型,如在上面的例子中,它的节点类型是Lambda;

Type:指表达式的静态类型,在上面的例子中,Type为Fun<int,int,int>。

在Expression Tree Visualizer中,我们可以看到表达式目录树的相关属性,如下图所示:

TerryLee_0163 

表达式目录树与委托

大家可能经常看到如下这样的语言,其中第一句是直接用Lambda表达式来初始化了Func委托,而第二句则使用Lambda表达式来构造了一个表达式目录树,它们之间的区别是什么呢?

static void Main(string[] args)
{
    Func<int, int, int> lambda = (a, b) => a + b * 2;

    Expression<Func<int, int, int>> expression = (a, b) => a + b * 2;
} 

其实看一下IL就很明显,其中第一句直接将Lambda表达式直接编译成了IL,如下代码所示:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  .maxstack  3
  .locals init ([0] class [System.Core]System.Func`3<int32,int32,int32> lambda)
  IL_0000:  nop
  IL_0001:  ldsfld     class [System.Core]System.Func`3<int32,int32,int32> 
                        TerryLee.LinqToLiveSearch.Program::'CS$<>9__CachedAnonymousMethodDelegate1'
  IL_0006:  brtrue.s   IL_001b
  IL_0008:  ldnull
  IL_0009:  ldftn      int32 TerryLee.LinqToLiveSearch.Program::'<Main>b__0'(int32,
                                                                             int32)
  IL_000f:  newobj     instance void class [System.Core]System.Func`3<int32,int32,int32>::.ctor(object,
                                                                                                native int)
  IL_0014:  stsfld     class [System.Core]System.Func`3<int32,int32,int32> 
                    TerryLee.LinqToLiveSearch.Program::'CS$<>9__CachedAnonymousMethodDelegate1'
  IL_0019:  br.s       IL_001b
  IL_001b:  ldsfld     class [System.Core]System.Func`3<int32,int32,int32> 
                    TerryLee.LinqToLiveSearch.Program::'CS$<>9__CachedAnonymousMethodDelegate1'
  IL_0020:  stloc.0
  IL_0021:  ret
}

而第二句,由于告诉编译器是一个表达式目录树,所以编译器会分析该Lambda表达式,并生成表示该Lambda表达式的表达式目录树,即它与我们手工创建表达式目录树所生成的IL是一致的,如下代码所示,此处为了节省空间省略掉了部分代码:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  .maxstack  4
  .locals init ([0] class [System.Core]System.Linq.Expressions.Expression`1<
                class [System.Core]System.Func`3<int32,int32,int32>> expression,
           [1] class [System.Core]System.Linq.Expressions.ParameterExpression CS$0$0000,
           [2] class [System.Core]System.Linq.Expressions.ParameterExpression CS$0$0001,
           [3] class [System.Core]System.Linq.Expressions.ParameterExpression[] CS$0$0002)
  IL_0000:  nop
  IL_0001:  ldtoken    [mscorlib]System.Int32
  IL_0006:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(...)
  IL_000b:  ldstr      "a"
  IL_0010:  call       class [System.Core]System.Linq.Expressions.ParameterExpression 
                        [System.Core]System.Linq.Expressions.Expression::Parameter(
                        class [mscorlib]System.Type,

  IL_0038:  call    class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle()
  IL_003d:  call    class [System.Core]System.Linq.Expressions.ConstantExpression 
                    [System.Core]System.Linq.Expressions.Expression::Constant(object,
                    class [mscorlib]System.Type)
  IL_0042:  call    class [System.Core]System.Linq.Expressions.BinaryExpression 
                    [System.Core]System.Linq.Expressions.Expression::Multiply(class [System.Core]System.Linq.Expressions.Expression,
                    class [System.Core]System.Linq.Expressions.Expression)
  IL_0047:  call    class [System.Core]System.Linq.Expressions.BinaryExpression
                    [System.Core]System.Linq.Expressions.Expression::Add(class [System.Core]System.Linq.Expressions.Expression,
                    class [System.Core]System.Linq.Expressions.Expression)
  IL_004c:  ldc.i4.2
  IL_004d:  newarr     [System.Core]System.Linq.Expressions.ParameterExpression
}

现在相信大家都看明白了,这里讲解它们的区别主要是为了加深大家对于表达式目录树的区别。

执行表达式目录树

前面已经可以构造出一个表达式目录树了,现在看看如何去执行表达式目录树。我们需要调用Compile方法来创建一个可执行委托,并且调用该委托,如下面的代码:

static void Main(string[] args)
{
    ParameterExpression paraLeft = Expression.Parameter(typeof(int), "a");
    ParameterExpression paraRight = Expression.Parameter(typeof(int), "b");

    BinaryExpression binaryLeft = Expression.Multiply(paraLeft, paraRight);
    ConstantExpression conRight = Expression.Constant(2, typeof(int));

    BinaryExpression binaryBody = Expression.Add(binaryLeft, conRight);

    Expression<Func<int, int, int>> lambda = 
        Expression.Lambda<Func<int, int, int>>(binaryBody, paraLeft, paraRight);

    Func<int, int, int> myLambda = lambda.Compile();

    int result = myLambda(2, 3);
    Console.WriteLine("result:" + result.ToString());

    Console.Read();
}

运行后输出的结果:

TerryLee_0159

这里我们只要简单的调用Compile方法就可以了,事实上在.NET Framework中是调用了一个名为ExpressionCompiler的内部类来做表达式目录树的执行(注意此处的Compiler不等同于编译器的编译)。另外,只能执行表示Lambda表达式的表达式目录树,即LambdaExpression或者Expression<TDelegate>类型。如果表达式目录树不是表示Lambda表达式,需要调用Lambda方法创建一个新的表达式。如下面的代码:

static void Main(string[] args)
{
    BinaryExpression body = Expression.Add(
        Expression.Constant(2),
        Expression.Constant(3));

    Expression<Func<int>> expression = 
        Expression.Lambda<Func<int>>(body, null);

    Func<int> lambda = expression.Compile();

    Console.WriteLine(lambda());
}

访问与修改表达式目录树

在本文一开始我就说过, 通过解析表达式目录树,我们可以实现一些特定功能,既然要解析表达式目录树,对于表达式目录树的访问自然是不可避免的。在.NET Framework中,提供了一个抽象的表达式目录树访问类ExpressionVisitor,但它是一个internal的,我们不能直接访问。幸运的是,在MSDN中微软给出了ExpressionVisitor类的实现,我们可以直接拿来使用。该类是一个抽象类,微软旨在让我们在集成ExpressionVisitor的基础上,实现自己的表达式目录树访问类。现在我们来看简单的表达式目录树:

static void Main(string[] args)
{
    Expression<Func<int, int, int>> lambda = (a, b) => a + b * 2;

    Console.WriteLine(lambda.ToString());
} 

输出后为:

TerryLee_0164

现在我们想要修改表达式目录树,让它表示的Lambda表达式为(a,b)=>(a - (b * 2)),这时就需要编写自己的表达式目录树访问器,如下代码所示:

public class OperationsVisitor : ExpressionVisitor
{
    public Expression Modify(Expression expression)
    {
        return Visit(expression);
    }

    protected override Expression VisitBinary(BinaryExpression b)
    {
        if (b.NodeType == ExpressionType.Add)
        {
            Expression left = this.Visit(b.Left);
            Expression right = this.Visit(b.Right);
            return Expression.Subtract(left,right);
        }

        return base.VisitBinary(b);
    }
}

使用表达式目录树访问器来修改表达式目录树,如下代码所示:

static void Main(string[] args)
{
    Expression<Func<int, int, int>> lambda = (a, b) => a + b * 2;

    var operationsVisitor = new OperationsVisitor();
    Expression modifyExpression = operationsVisitor.Modify(lambda);

    Console.WriteLine(modifyExpression.ToString());
}

运行后可以看到输出:

TerryLee_0165

似乎我们是修改表达式目录树,其实也不全对,我们只是修改表达式目录树的一个副本而已,因为表达式目录树是不可变的,我们不能直接修改表达式目录树,看看上面的OperationsVisitor类的实现大家就知道了,在修改过程中复制了表达式目录树的节点。

为什么需要表达式目录树

通过前面的介绍,相信大家对于表达式目录树已经有些了解了,还有一个很重要的问题,就是为什么需要表达式目录树?在本文开始时,就说过通过解析表达式目录树,可以实现我们一些特定的功能,就拿LINQ to SQL为例,看下面这幅图:

TerryLee_0167

当我们在C#语言中编写一个查询表达式时,它将返回一个IQueryable类型的值,在该类型中包含了两个很重要的属性Expression和Provider,如下面的代码:

TerryLee_0168

我们编写的查询表达式,将封装为一种抽象的数据结构,这个数据结构就是表达式目录树,当我们在使用上面返回的值时,编译器将会以该值所期望的方式进行翻译,这种方式就是由Expression和Provider来决定。可以看到,这样将会非常的灵活且具有良好的可扩展性,有了表达式目录树,可以自由的编写自己的Provider,去查询我们希望的数据源。经常说LINQ为访问各种不同的数据源提供了一种统一的编程方式,其奥秘就在这里。然而需要注意的是LINQ to Objects并不需要任何特定的LINQ Provider,因为它并不翻译为表达式目录树,后面会说到这一点。

总结

本为详细介绍了表达式目录树的相关知识,为我们编写自己的LINQ Provider打下一个基础,希望对于大家有所帮助。查看目前网上的各种lINQ Provider,请访问万般皆LINQ

相关文章:打造自己的LINQ Provider(中):IQueryable和IQueryProvider

作者:TerryLee
出处:http://terrylee.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted @ 2008-08-01 01:19 TerryLee 阅读(9880) 评论(81)  编辑 收藏 网摘 所属分类: [06]  LINQ之美

  回复  引用  查看    
#1楼 2008-08-01 01:24 | Jeffrey Zhao      
支持。
就算不LINQ,Lambda表达式也是一种很容易表达语义的做法,因此学会解析Expression是很重要的。
  回复  引用  查看    
#2楼 2008-08-01 01:29 | 怪怪      
这篇文章清晰明了, 极好~
  回复  引用  查看    
#3楼 2008-08-01 01:31 | Caling Xie      
不错!
  回复  引用  查看    
#4楼 [楼主]2008-08-01 01:32 | TerryLee      
@Jeffrey Zhao
老赵够快的:)
的确是,学会解析Expression Tree相当重要……
  回复  引用  查看    
#5楼 [楼主]2008-08-01 01:33 | TerryLee      
@怪怪
这个时间正是老怪最活跃的时间,呵呵
现在感觉很多朋友对于LINQ方面的概念有些混淆,所以想通过这个系列来解释一下:)
  回复  引用  查看    
#6楼 [楼主]2008-08-01 01:33 | TerryLee      
@Caling Xie
谢谢支持:-)
  回复  引用  查看    
#7楼 2008-08-01 01:39 | Jeffrey Zhao      
--引用--------------------------------------------------
怪怪: 这篇文章清晰明了, 极好~
--------------------------------------------------------
而且图文并茂——这点老赵就懒得放图……
  回复  引用  查看    
#8楼 2008-08-01 01:43 | 怪怪      
@Jeffrey Zhao
你至少还有这个能力, 我就画不出来图, 唯一一次, 还是我最喜欢的画图板....

对设计模式Fans附加一个提示: LZ的文章可以结合解释器模式一起看~~
  回复  引用  查看    
#9楼 [楼主]2008-08-01 01:45 | TerryLee      
@Jeffrey Zhao
@怪怪
两位都是夜猫子,我去睡觉先^_^
  回复  引用  查看    
#10楼 2008-08-01 02:42 | 东哥      
留个名,明天 接着看下集啊.
  回复  引用  查看    
#11楼 2008-08-01 02:57 | U2U      
进来难得一见的好文章
  回复  引用  查看    
#12楼 2008-08-01 07:46 | 小生      
寫得非常好﹐不錯﹗
順便問下樓主﹐可不可以通過讀取字符串"(a, b) => a + b * 2"來構建Expression呀?
  回复  引用    
#13楼 2008-08-01 08:10 | 5254341 [未注册用户]
很不错。
TerryLee老大准备要写一个系列吗?
期待。。。
支持。。。
  回复  引用  查看    
#14楼 2008-08-01 08:10 | 木野狐(Neil Chen)      
收藏先.
  回复  引用  查看    
#15楼 2008-08-01 08:21 | 雅阁布      
关注MVP们!!!
  回复  引用  查看    
#16楼 2008-08-01 08:30 | 横刀天笑      
老大的文章真是图文并茂啊,真可称之为
奇文共欣赏,疑义相与析,呵呵

PS:正在写Linq系列,大概再过两篇也要到表达式树了,你发了这篇我都不知道怎么写了,呵呵
  回复  引用  查看    
#17楼 2008-08-01 08:54 | jillzhang      
感觉linq语法怪怪的,做个动态排序都比较麻烦,不知道好在哪里?
  回复  引用  查看    
#18楼 2008-08-01 08:54 | 王孟军!      
老兄,我请教你一个非技术问题

1.一天睡几个小时?
2.厕所里是否有几本技术书,或者电脑?
3.还有,你老婆要你陪她逛街,咋办?
4....^-^
  回复  引用  查看    
#19楼 2008-08-01 08:57 | xjb      
嗯,关注这个系列
  回复  引用    
#20楼 2008-08-01 09:15 | 5254341 [未注册用户]
我比对大牛们的私生活感兴趣。哈哈
--------------------------------------------------
老兄,我请教你一个非技术问题

1.一天睡几个小时?
2.厕所里是否有几本技术书,或者电脑?
3.还有,你老婆要你陪她逛街,咋办?
4....^-^
  回复  引用    
#21楼 2008-08-01 09:40 | 幸福 [未注册用户]
@terrylee
好像开始对你的私生活感兴趣了
  回复  引用    
#22楼 2008-08-01 09:44 | 幸福 [未注册用户]
@terrylee
俺再回去补个觉
  回复  引用  查看    
#23楼 2008-08-01 09:50 | JimLiu      
好文,支持一下!
  回复  引用  查看    
#24楼 [楼主]2008-08-01 09:59 | TerryLee      
@东哥
呵呵,准备分为几个部分来写,第二篇正在构思中……
  回复  引用  查看    
#25楼 [楼主]2008-08-01 10:00 | TerryLee      
@U2U
客气了o(∩_∩)o...
  回复  引用  查看    
#26楼 2008-08-01 10:00 | JerryShi      
我是个.Net大洋里的小菜鸟,之前因为要做一个silverlight的小应用,项目组里的朋友介绍来TerryLee的博客上学习,现在我发现写博客对我自己来讲是个提高的过程,所以也会常来看看你。
呵呵,Terry的精神和知识我可能拍马也赶不上了,只好在后边当一个扛旗的小兵!
现在要学的东西实在太多了,只好从2.0里的基础学起!唉,不知道何年何月才会达到Terry今天的境界。
期待!
  回复  引用  查看    
#27楼 [楼主]2008-08-01 10:01 | TerryLee      
@小生
肯定是可以的,通过解析该字符串来构造Expression Tree,不过可能麻烦一些,需要涉及到运算符的优先级等问题
  回复  引用  查看    
#28楼 [楼主]2008-08-01 10:02 | TerryLee      
@5254341
是的,是一个系列但不会太多,不过每篇文章估计会多写一些东西:)
  回复  引用  查看    
#29楼 [楼主]2008-08-01 10:02 | TerryLee      
@木野狐(Neil Chen)
:)
  回复  引用  查看    
#30楼 [楼主]2008-08-01 10:02 | TerryLee      
@雅阁布
谢谢关注:-)
  回复  引用  查看    
#31楼 [楼主]2008-08-01 10:03 | TerryLee      
@横刀天笑
老兄过奖了,其实还是有很多东西写的,感觉这篇还是没有写完……:)
  回复  引用  查看    
#32楼 [楼主]2008-08-01 10:04 | TerryLee      
@jillzhang
其实有一点就够了:访问多种数据源使用同一种方式,呵呵
  回复  引用  查看    
#33楼 [楼主]2008-08-01 10:06 | TerryLee      
@王孟军!
老兄给我出难题了,呵呵,这几个问题都不好回答啊,不过我还是试着回答一下
1.一天6个小时吧
2.厕所里有书,但不是技术书,没有电脑
3.老婆想逛街,那就陪她去,老婆的命令要无条件执行:)

  回复  引用  查看    
#34楼 [楼主]2008-08-01 10:06 | TerryLee      
@幸福
嗯……
  回复  引用    
#35楼 2008-08-01 10:06 | 搞搞笑 [未注册用户]
@王孟军!
我来回答你
1:一般是晚上2点半到早上7点半+中午1小时共6个小时,够了!
2:厕所有基本书,但是<秀><绝色>之类的
3:她敢吗?只有我说"去帮我买XX",她才敢去逛街
4....^-^

  回复  引用  查看    
#36楼 [楼主]2008-08-01 10:06 | TerryLee      
@JimLiu
谢谢支持啊:)
  回复  引用  查看    
#37楼 [楼主]2008-08-01 10:08 | TerryLee      
@JerryShi
呵呵,太过谦了,慢慢来,最重要的是打好基础……
  回复  引用  查看    
#38楼 [楼主]2008-08-01 10:09 | TerryLee      
@搞搞笑
老兄这是说你还是说我呢?呵呵:)
  回复  引用  查看    
#39楼 2008-08-01 10:37 | davin      
刚刚离开校园参加工作,由于需要,学习了Entity data model ...,从framwork2.0 到framework3.5的跳跃,需要学习的东西太多了,幸好还有大牛们的无私奉献
文章写得很受用!期待续篇

  回复  引用    
#40楼 2008-08-01 10:37 | 幸福 [未注册用户]
--引用--------------------------------------------------
TerryLee: @王孟军!
3.老婆想逛街,那就陪她去,老婆的命令要无条件执行:)

--------------------------------------------------------
嗯?
  回复  引用  查看    
#41楼 [楼主]2008-08-01 10:53 | TerryLee      
  回复  引用  查看    
#42楼 [楼主]2008-08-01 10:54 | TerryLee      
@davin
太客气了……:)
  回复  引用    
#43楼 2008-08-01 10:58 | Waitd Ding(noLogin) [未注册用户]
支持支持再支持,
学习学习再学习:)
  回复  引用  查看    
#44楼 2008-08-01 10:59 | 在路上的牛      
Expression Tree很重要,以前研究过,没研究透,继续学习
  回复  引用  查看    
#45楼 2008-08-01 11:29 | 王德水      
绝对支持,希望开个系列,大侠每次都是由浅入深,这次好像直接到深了
  回复  引用  查看    
#46楼 [楼主]2008-08-01 11:48 | TerryLee      
@Waitd Ding(noLogin)
感动中……呵呵
  回复  引用  查看    
#47楼 [楼主]2008-08-01 11:49 | TerryLee      
@在路上的牛
是的,就算不自定义LINQ Provider,对于我们认识LINQ还是挺有帮助的:)
  回复  引用  查看    
#48楼 [楼主]2008-08-01 11:50 | TerryLee      
@王德水
这本来就是一个系列啊,不过可能很短
因为LINQ的使用网上已经非常多了,呵呵
  回复  引用  查看    
#49楼 2008-08-01 11:54 | onekey      
var operationsVisitor = new OperationsVisitor();
Expression modifyExpression = operationsVisitor.Modify(lambda);
=================================================

这个modifyExpression表达式如何使用?
  回复  引用  查看    
#50楼 [楼主]2008-08-01 11:59 | TerryLee      
@onekey
没看懂你说的如何表示是什么意思?呵呵

在Modify方法中,我们只是修改了一下表达式的操作符,返回的仍然是一个表达式。
  回复  引用    
#51楼 2008-08-01 12:53 | copy123 [未注册用户]
其实创建一个Linq的Provider很容易. 几十行代码就能做一个.
随便上google上找 custom linq provider 就有一大堆.

但做Linq的Provider麻烦之处就是,

你不一定能支持所有的语法. 就连Linq2SQL也不支持某些情况.
因为某些C#的语法, 是无法用T-SQL实现的.

所以, 做一个简单的Provider,可以支持一些简单的大小比较的查询.

目前Linq无法在编译时期知道某个Provider是否支持某些语法.
很多语句一定要到执行期间, 才被Provider告诉不支持然后抛出错误.

这是Linq的缺陷.


  回复  引用  查看    
#52楼 2008-08-01 13:08 | 横刀天笑      
@copy123
其实这也不是Linq的缺陷,可以说是Linq的特性之一:延迟计算,如果没有延迟计算,我想Linq的性能更加要大打折扣
  回复  引用  查看    
#53楼 2008-08-01 13:15 | 侯垒      
还没有学习这些东西.不过先支持了再学习.
  回复  引用  查看    
#54楼 [楼主]2008-08-01 13:24 | TerryLee      
@copy123
其实我觉的这反倒是LINQ的优势……

如果想写一个简单的LINQ Provider,也许几十行代码就够了,但我不知道这样的LINQ Provider意义何在。另外,定义LINQ Provider我觉的掌握的Expression Tree的解析相当重要:)
  回复  引用  查看    
#55楼 [楼主]2008-08-01 13:24 | TerryLee      
@侯垒
谢谢支持,呵呵:)
  回复  引用  查看    
#56楼 2008-08-01 15:30 | 毁于随      
非常好的文章.看来这个东西还是可以学会的,用不着把编译原理拿出来先搞搞.
  回复  引用    
#57楼 2008-08-01 17:12 | wbixd [未注册用户]
TerryLee老大,问个与这个问题不相关的问题,在silverlight2 beta2中如何控制视频的暂停 播放 停止呢 就是MediaElement这个控件
我设置了 MediaElement.stop();却没有反映 查了下 之前是将MediaElement.的 LoadedBehavior 设置为 Manual,才能使用 Play、Pause 和 Stop 方法以交互方式控制媒体。

实在找不到方法 所以在这里求助你
  回复  引用  查看    
#58楼 2008-08-01 17:23 | JacksonLin      

有一点不明白的.
LinQ固然强大,但为什么只开放linq provider?
就像select from where这些关键字也一同开放?

provider虽然扩展性很好,但也被语法结构绑死.
就像这样:
"create folder "C:/folder" "
如果我们能够实现 create folder这些关键字和这些关键字的provider
不是我们程序员的理想吗?(DSL)

  回复  引用  查看    
#59楼 2008-08-01 17:25 | JacksonLin      
请楼主回答.
我已经拜访几位MVP老兄了.
  回复  引用  查看    
#60楼 2008-08-01 17:34 | 横刀天笑      
@JacksonLin
添加关键字肯定是不可能的,添加了关键字需要修改编译器,这个是扩展provider无法做到的,不过你可以通过扩展方法做到一个比较优雅的语法,如果真的要达到你这样的语法,我觉得,C#要能像ruby那样的语言一样,方法调用的时候可以不写括号,这样模拟出一种像是关键字的感觉
  回复  引用  查看    
#61楼 [楼主]2008-08-01 17:43 | TerryLee      
@毁于随
谢谢支持:)

解析Expression Tree倒是用不到编译原理……
  回复  引用  查看    
#62楼 [楼主]2008-08-01 17:45 | TerryLee      
@wbixd
设置一下MediaElement的LoadedBehavior属性不行吗?在XAML中
另外这里有一个Silverlight Q&A专贴,有问题我们可以去哪儿讨论:)
http://www.cnblogs.com/Terrylee/archive/2008/07/21/silverlight-2-articles.html
  回复  引用  查看    
#63楼 [楼主]2008-08-01 17:48 |