把匿名对象转换成成员名索引形式的动态对象
2011-08-11 15:51
363 查看
在用LINQ进行数据查询操作时,我们时不时的会碰到匿名对象,对匿名对象的操作有时候会很不方便比如
var query = from row in table select new { Col1 = row.col1 };
这时只在当前方法内用query的结果是没问题的,但如果要把query传出去用,方法就要用到这么个声明形式
IQueryable<dynamic> GetCol1()
这样方法外才能这样做
query.First().Col1
似乎很不错了。但是实际开发中还会碰到一种情况,就是字段名本身都是动态传入的,这样一来dynamic也无计可施了,碰到这种情况大多数都会选择尝试用反射来读出匹配的数据,对于这种情况如果dynamic类型能支持query.First()["Col1"]这种索引表示方式就方便多了,这里就介绍一种简单有效的实现。
首先是让dynmaic对象支持[]索引器,这个非常简单,只要继承自DynamicObject对象重写掉2个方法就行了,这里我们先暂时只支持一个键的索引:
dynamic d = new OneKeyDictionaryObject();
d["Col1"] = "test";
var col1 = d["Col1"];
确定可以正常工作。
有了这个我们只要简单的把匿名对象的每个属性放到对应索引里就行了,用反射?不,这里采用了构造一个表达式来把匿名类型转换成我们定义的动态类型。
先写一个简单的lambda:
Expression<Func<dynamic, dynamic>> f = o => {
dynamic d = new OneKeyDictionaryObject();
d["Col1"] = o.Col1;
return d;
};
遗憾的是这个表达式是无法通过编译的,因为C#编译器不支持编译带语句体、赋值运算符和dynamic类型的表达式。不过不要放弃,编译器不支持没关系,Framework支持就行,手工构建一个:
最后的func就是lambda编译后的delegate,这个构建出来的表达式样子大致就是
Expression<Func<dynamic, dynamic>> f = o => {
dynamic d = new OneKeyDictionaryObject();
d["Col1"] = o.Col1;
d["Col2"] = o.Col2;
d["Col3"] = o.Col3;
...
return d;
};
试一下:
var o = new { Col1 = 1, Col2 = 2 };
dynamic d = func(o);
Console.WriteLine("{0}, {1}", d["Col1"], d["Col2“]);
关于如何用表达式表示一个dynamic操作,参考了http://d.hatena.ne.jp/NetSeed/里的内容。
var query = from row in table select new { Col1 = row.col1 };
这时只在当前方法内用query的结果是没问题的,但如果要把query传出去用,方法就要用到这么个声明形式
IQueryable<dynamic> GetCol1()
这样方法外才能这样做
query.First().Col1
似乎很不错了。但是实际开发中还会碰到一种情况,就是字段名本身都是动态传入的,这样一来dynamic也无计可施了,碰到这种情况大多数都会选择尝试用反射来读出匹配的数据,对于这种情况如果dynamic类型能支持query.First()["Col1"]这种索引表示方式就方便多了,这里就介绍一种简单有效的实现。
首先是让dynmaic对象支持[]索引器,这个非常简单,只要继承自DynamicObject对象重写掉2个方法就行了,这里我们先暂时只支持一个键的索引:
public class OneKeyDictionaryObject : DynamicObject { Dictionary<dynamic, object> table = new Dictionary<dynamic, object>(); public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) { if (indexes.Length == 1) { table[indexes[0]] = value; return true; } return base.TrySetIndex(binder, indexes, value); } public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) { if (indexes.Length == 1) { result = table[indexes[0]]; return true; } return base.TryGetIndex(binder, indexes, out result); } }
dynamic d = new OneKeyDictionaryObject();
d["Col1"] = "test";
var col1 = d["Col1"];
确定可以正常工作。
有了这个我们只要简单的把匿名对象的每个属性放到对应索引里就行了,用反射?不,这里采用了构造一个表达式来把匿名类型转换成我们定义的动态类型。
先写一个简单的lambda:
Expression<Func<dynamic, dynamic>> f = o => {
dynamic d = new OneKeyDictionaryObject();
d["Col1"] = o.Col1;
return d;
};
遗憾的是这个表达式是无法通过编译的,因为C#编译器不支持编译带语句体、赋值运算符和dynamic类型的表达式。不过不要放弃,编译器不支持没关系,Framework支持就行,手工构建一个:
var elementType = o.GetType(); var properties = elementType.GetProperties(); var setBinder = Binder.SetIndex(CSharpBinderFlags.ResultIndexed, typeof(Program), new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant | CSharpArgumentInfoFlags.UseCompileTimeType, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant | CSharpArgumentInfoFlags.UseCompileTimeType, null) }); var inputParameter = Expression.Parameter(typeof(object), "o"); var localVariable = Expression.Variable(typeof(object), "d"); var assign = Expression.Assign(localVariable, Expression.New(typeof(OneKeyDictionaryObject))); List<Expression> setters = new List<Expression>(); foreach (var property in properties) { var name = property.Name; var indexer = Expression.Constant(name); var getBinder = Binder.GetMember(CSharpBinderFlags.None, name, typeof(Program), new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) }); var getter = Expression.Dynamic(getBinder, typeof(object), inputParameter); var setter = Expression.MakeDynamic(typeof(Action<CallSite, object, object, object>), setBinder, localVariable, indexer, getter); setters.Add(setter); } var lable = Expression.Label(typeof(object), "ret"); IEnumerable<Expression> body = new[] { assign }.Union(setters) .Union( new Expression[] { Expression.Return(lable, localVariable, typeof(object)), Expression.Label(lable, localVariable) }); IEnumerable<ParameterExpression> variables = new ParameterExpression[] { localVariable }; var block = Expression.Block( typeof(object), variables, body ); var lambda = Expression.Lambda<Func<dynamic, dynamic>>(block, inputParameter); var func = lambda.Compile();
最后的func就是lambda编译后的delegate,这个构建出来的表达式样子大致就是
Expression<Func<dynamic, dynamic>> f = o => {
dynamic d = new OneKeyDictionaryObject();
d["Col1"] = o.Col1;
d["Col2"] = o.Col2;
d["Col3"] = o.Col3;
...
return d;
};
试一下:
var o = new { Col1 = 1, Col2 = 2 };
dynamic d = func(o);
Console.WriteLine("{0}, {1}", d["Col1"], d["Col2“]);
关于如何用表达式表示一个dynamic操作,参考了http://d.hatena.ne.jp/NetSeed/里的内容。
相关文章推荐
- warning C4407: 在指向成员表示形式的不同指针之间进行转换,编译器可能生成不正确的代码
- js动态获取当前系统时间+js字符串转换为date日期对象
- Java基础05:面向对象;类与对象;匿名对象;成员变量;局部变量;封装;构造函数
- 局部变量与成员变量的区别、匿名对象
- javascript对象转换,动态属性取值
- objective-c运行时机制runtime,动态获取对象的属性和成员变量
- JS中常用操作技巧(页面动态增加div,contain方法扩展,JSON对象鱼字符串转换)
- 如何动态保存不同类的对象的成员函数的地址?--解决
- warning C4407: 在指向成员表示形式的不同指针之间进行转换,编译器可能生成不正确的代码
- COM动态添加删除成员,类似JavaScript中调用的对象
- 02、J2SE基础-对象类型的转换、多态性、匿名内部类
- 黑马程序员_06_面向对象_成员变量_匿名对象_封装_构造函数_thisPrivate
- C++ 转换类型运算符 调用构造函数将变量转化成为一个对象的成员变量
- Swift动态获取成员变量及对象关联
- C# 匿名对象(匿名类型)、var、动态类型 dynamic
- warning C4407: 在指向成员表示形式的不同指针之间进行转换,编译器可能生成不正确的代码
- 将Object转换成动态指定的对象,将一个实例引用指向一个Object引用
- js动态的使用对象成员&遍历对象属性值
- MFC自定义类中类成员包含动态指针时类对象的“=”赋值操作
- C++匿名对象调用成员函数