本文介绍 如何编写表达式语句块 操作“数组与索引器”,比如如何获取或设置数组元素 由或者如何设置索引器元素 然后打印其输出值
OK...那么我们先看看如何从一个数组元素中获取其值,从本文开始将不会在编写Lambda express块 由编译器编译转换 只需要写表达式就够了, so...
static void Main() { ParameterExpression s = Expression.Parameter(typeof(string[])); Func<string[], string> f = Expression.Lambda<Func<string[], string>>( Expression.ArrayIndex(s, Expression.Constant(0)), s ).Compile(); string r = f(new string[] { "A Place Nearby" }); Console.WriteLine(r); }上述定义了一个“Func<string[], string>”委托类型的动态表达式方法 它内部的代码很简单 只是像样的访问了“string[] s;”的第一个元素 索引为“0” 那么上述的表达式代码可否访问对象的“索引器” 而不是“数组的索引”呢?或许你可以用如下的代码进行一次测试
static void Main() { ParameterExpression s = Expression.Parameter(typeof(string[])); Func<IList<string>, string> f = Expression.Lambda<Func<IList<string>, string>>( Expression.ArrayIndex(s, Expression.Constant(0)), s ).Compile(); string r = f(new string[] { "A Place Nearby" }); Console.WriteLine(r); }上述代码无法在运行时编译,对象索引器与数组索引是两个不同的东西 不论是从概念上还是从下层角度上看 即使你可以确定输入IList<string>是一个“string[]”数组, 那么如何通过对象索引器获取元素?
ParameterExpression s = Expression.Parameter(typeof(IList<string>)); Func<IList<string>, string> f = Expression.Lambda<Func<IList<string>, string>>( Expression.MakeIndex(s, typeof(IList<string>).GetProperty("Item"), new[] { Expression.Constant(0) } ), s ).Compile(); string r = f(new string[] { "A Place Nearby" }); Console.WriteLine(r);在上述的代码中,它与上面相同输入了一个有效的“string[]”数组 不过有一些不同之处在于它不在通过“Expression.ArrayIndex”访问数组成员 而是改由“Expression.MakeIndex”索引器方式访问元素
Note: 在C#/VB.NET中具备对象的索引器 但这只是语法层面对的支持 对于下层而言并不存在索引器的概念 它只是一个属性 一般“.NET/COM”中对于标识对象索引(一元运算符重载)取函数名为Item,这也是为什么在JS中你可以通过“Item”或者“[]”访问一个DOM对象的元素 对于重载[]运算符 那么对于重载代码函数被命名潜规则一般为“Item”
上面提到一般潜规则取名为“Item” 那么只需要主动获取IList<T>的“Item”属性即可 或者主动获取对应的方法 对象索引器与数组索引是两个不同的东西 此处会看的更加形象
那么如何设置数组元素与索引器的值?实际上你会发现它们大同小异 这是.net/expression相对于msil很出色的地方 准确的说我很厌倦那种很麻烦的方式
举个小例子:假设只有一个局部变量定义了一个int[]的数组 现在需要获取这个数组中的第三个元素 那么需要怎么做?
ldarg.0 // 先压入数组
ldc.i4.2 // 压入索引二
ldelem.i4 // 先清计算栈栈,压入数组索引二的成员
从上述的代码中是否发现 它很麻烦?是的它机械式的方式很令人讨厌 但这的确是最适应给“JIT”的解析编译的最佳方式 这与汇编相比则很类似
static void Main() { ParameterExpression s = Expression.Parameter(typeof(string[])); Action<string[]> f = Expression.Lambda<Action<string[]>>( Expression.Assign( Expression.ArrayAccess(s, Expression.Constant(0)), Expression.Constant("you just fadded away") ), s ).Compile(); string[] m = new string[] { "A Place Nearby" }; f(m); Console.WriteLine(m[0]); }从上面中的代码中看到设置数组成员代码与上面的代码基本都大同小易 但也有一些变动设置数组元素是通过“Assign & ArrayAccess”完成的 为什么不通过“ArrayIndex”呢? 这是因为ArrayIndex只允许访问索引位置的成员 而不允许设置其索引位置成员的内容但ArrayAccess则不存在这些限制 ArrayAccess它可以设置也可以访问、
static void Main() { ParameterExpression s = Expression.Parameter(typeof(IList<string>)); Action<IList<string>> f = Expression.Lambda<Action<IList<string>>>( Expression.Assign( Expression.MakeIndex(s, typeof(IList<string>).GetProperty("Item"), new[] { Expression.Constant(0) } ), Expression.Constant("you just fadded away") ), s ).Compile(); string[] m = new string[] { "A Place Nearby" }; f(m); Console.WriteLine(m[0]); }对象索引器则并没有什么变动 原先是怎么访问数组的就怎么去设置数组 相对于Array它相对的比较统一 如何要访问或设置对象索引器的元素 必须通过“MakeIndex”