.NET Framework 4.0 Beta 1里的Expression Tree一例

xiaoxiao2026-05-16  0

既然[url=http://rednaxelafx.iteye.com/blog/391598]装上了Visual Studio 2010 Beta 1[/url],正好可以试试.NET Framework 4.0里的一些新东西。我比较关注的是Expression Tree的部分,到底哪些功能进到了.NET 4,哪些还得到[url=http://dlr.codeplex.com]CodePlex的DLR站[/url]上去找呢?试用一下找找感觉。 我暂时没试这个beta里的C#对dynamic的支持,是因为暂时还没想到啥有趣的场景能写点简单的代码来玩的。对.NET类型故意使用dynamic的玩法在之前CTP的时候就玩过了,不过瘾了。回头针对.NET 4来编译一个IronPython来看看,到时就能好好把玩一番dynamic了。 回到Expression Tree。在.NET Framework 4.0里它叫Expression Tree v2,简称ETv2。它兼容于.NET Framework 3.5里LINQ的Expression Tree,但实际上是从DLR的DLR tree发展而来的。时至今日DLR的代码仍在快速变化中,而ETv2作为LINQ与DLR的公共部分要放到标准库里,等不到DLR稳定下来。折中的解决方案就是在标准库里的版本砍掉一些DLR里还没定下来的东西和低优先级的东西。像是LoopExpression进入了标准库,但特化版本的ForEach、While等就只有CodePlex上的版本才有。 .NET Framework 4.0中,ETv2位于System.Core.dll程序集中,在System.Linq.Expressions命名空间下。CodePlex的DLR的ETv2则位于Microsoft.Scripting.Core.dll程序集中,Microsoft.Linq.Expressions命名空间下。CodePlex的DLR之所以要用不同的命名空间是为了避免与标准库冲突,但这样一来由编译器生成的ET就与CodePlex的DLR中的ET不兼容了。所以我才那么期待.NET 4.0赶紧出……为了能用上标准库里的ETv2。 昨天装好VS2010 Beta后写的代码如下。 [img]http://rednaxelafx.iteye.com/upload/attachment/105422/2b8d5f48-6ce0-357f-a9ba-39f235a3c7a8.png[/img] 就是先做了个简单的in-memory LINQ查询,然后用ETv2来构造出一个遍历并输出查询结果的函数,并调用之。 using System;using System.Collections;using System.Collections.Generic;using System.Linq;using System.Linq.Expressions;namespace ConsoleApplication1 { static class Program { static void Main(string[] args) { var list = from i in Enumerable.Range(0, 100) where i % 9 == 0 orderby i descending select i; var vIter = Expression.Variable(typeof(IEnumerator<int>), "iter"); var vI = Expression.Variable(typeof(int), "i"); var lBreak = Expression.Label(); var eForeach = Expression.Lambda<Action>( Expression.Block( new[] { vIter, vI }, // IEnumerator<int> iter; int i; Expression.Assign( // iter = list.GetEnumerator(); vIter, Expression.Call( Expression.Constant(list), typeof(IEnumerable<int>).GetMethod("GetEnumerator"))), Expression.Loop( // while (true) Expression.IfThenElse( // if Expression.Call( // (iter.MoveNext()) vIter, typeof(IEnumerator).GetMethod("MoveNext")), Expression.Block( // { Expression.Assign( // i = iter.Current; vI, Expression.Property(vIter, "Current")), Expression.Call( // Console.WriteLine(i); typeof(Console).GetMethod("WriteLine", new[] { typeof(int) }), new[] { vI })), Expression.Break(lBreak)), // } else break; } lBreak)), new ParameterExpression[0]); eForeach.Compile()(); } }} 用ETv2构造出来的函数基本等价于一个含有普通foreach循环的lambda: () => { foreach (var i in list) { Console.WriteLine(i); }} 注意到foreach循环可以被展开为while循环: () => { var iter = list.GetEnumerator(); while (iter.MoveNext()) { var i = iter.Current; Console.WriteLine(i); }} 我用ETv2实现的lambda实际上是这样的: () => { var iter = list.GetEnumerator(); int i; while (true) if (iter.MoveNext()) { i = iter.Current; Console.WriteLine(i); } else break;} 后来想了想,我应该把vI放在if里的block来声明的,会更符合foreach的语义。不过懒得开虚拟机去改了…… 写成while (true)是因为ETv2里的LoopExpression就是代表一个无限循环,外加用户可自定义的条件分支及跳转目标(BreakTarget和ContinueTarget)。在C-like语言里,基本循环结构可以分为条件前置的while/for和条件后置的do...while两种。但总有些时候我们希望既不是在开头也不是在结尾,而是在循环体的中间来判断循环条件;在C-like语言里我们就只好用无限循环+条件语句来模拟这种半中腰的循环结构。ETv2为了提供最大的弹性,提供的基本循环结构就是这种代表无限循环的LoopExpression。但这样的基本结构用起来总让人嫌麻烦,还好CodePlex上的DLR里有特化版本的循环结构,只是没赶上.NET 4这趟车而已。
转载请注明原文地址: https://www.6miu.com/read-5048834.html

最新回复(0)