Yur*_*rik 5 .net c# expression tuples
对于远程处理方案,结果将非常好地作为数组或Tuple对象列表接收(在强类型的好处之中).
示例: 动态转换SELECT Name, Age FROM Table => List<Tuple<string,int>>
问题:是否存在任何样本,给定任意数据表(如SQL结果集或CSV文件),每个列的类型仅在运行时知道,以生成将动态创建强类型List<Tuple<...>>对象的代码.代码应该动态生成,否则会非常慢.
Dom*_*etz 11
编辑:我更改了代码以使用Tuple构造函数而不是Tuple.Create.它目前仅适用于最多8个值,但添加"元组堆叠"应该是微不足道的.
这有点棘手,实现依赖于数据源.为了给人留下印象,我使用匿名类型列表作为源创建了一个解决方案.
正如Elion所说,我们需要动态创建一个表达式树,然后再调用它.我们采用的基本技术称为投影.
我们必须在运行时获取类型信息,并根据属性计数创建Tuple(...)构造函数的ConstructorInfor .每次调用都是动态的(尽管每条记录需要相同).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
class Program
{
static void Main(string[] args)
{
var list = new[]
{
//new {Name = "ABC", Id = 1},
//new {Name = "Xyz", Id = 2}
new {Name = "ABC", Id = 1, Foo = 123.22},
new {Name = "Xyz", Id = 2, Foo = 444.11}
};
var resultList = DynamicNewTyple(list);
foreach (var item in resultList)
{
Console.WriteLine( item.ToString() );
}
Console.ReadLine();
}
static IQueryable DynamicNewTyple<T>(IEnumerable<T> list)
{
// This is basically: list.Select(x=> new Tuple<string, int, ...>(x.Name, x.Id, ...);
Expression selector = GetTupleNewExpression<T>();
var expressionType = selector.GetType();
var funcType = expressionType.GetGenericArguments()[0]; // == Func< <>AnonType..., Tuple<String, int>>
var funcTypegenericArguments = funcType.GetGenericArguments();
var inputType = funcTypegenericArguments[0]; // == <>AnonType...
var resultType = funcTypegenericArguments[1]; // == Tuple<String, int>
var selects = typeof (Queryable).GetMethods()
.AsQueryable()
.Where(x => x.Name == "Select"
);
// This is hacky, we just hope the first method is correct,
// we should explicitly search the correct one
var genSelectMi = selects.First();
var selectMi = genSelectMi.MakeGenericMethod(new[] {inputType, resultType});
var result = selectMi.Invoke(null, new object[] {list.AsQueryable(), selector});
return (IQueryable) result;
}
static Expression GetTupleNewExpression<T>()
{
Type paramType = typeof (T);
string tupleTyneName = typeof (Tuple).AssemblyQualifiedName;
int propertiesCount = paramType.GetProperties().Length;
if ( propertiesCount > 8 )
{
throw new ApplicationException(
"Currently only Tuples of up to 8 entries are alowed. You could change this code to allow stacking of Tuples!");
}
// So far we have the non generic Tuple type.
// Now we need to create select the correct geneeric of Tuple.
// There might be a cleaner way ... you could get all types with the name 'Tuple' and
// select the one with the correct number of arguments ... that exercise is left to you!
// We employ the way of getting the AssemblyQualifiedTypeName and add the genric information
tupleTyneName = tupleTyneName.Replace("Tuple,", "Tuple`" + propertiesCount + ",");
var genericTupleType = Type.GetType(tupleTyneName);
var argument = Expression.Parameter(paramType, "x");
var parmList = new List<Expression>();
List<Type> tupleTypes = new List<Type>();
//we add all the properties to the tuples, this only will work for up to 8 properties (in C#4)
// We probably should use our own implementation.
// We could use a dictionary as well, but then we would need to rewrite this function
// more or less completly as we would need to call the 'Add' function of a dictionary.
foreach (var param in paramType.GetProperties())
{
parmList.Add(Expression.Property(argument, param));
tupleTypes.Add(param.PropertyType);
}
// Create a type of the discovered tuples
var tupleType = genericTupleType.MakeGenericType(tupleTypes.ToArray());
var tuplConstructor =
tupleType.GetConstructors().First();
var res =
Expression.Lambda(
Expression.New(tuplConstructor, parmList.ToArray()),
argument);
return res;
}
}
Run Code Online (Sandbox Code Playgroud)
如果要使用DataReader或某些CVS输入,则需要重写GetTupleNewExpression函数.
我不能谈论性能,虽然它作为本机LINQ实现应该不会慢得多,因为LINQ表达式的生成仅在每次调用时发生一次.如果它太慢,你可以继续生成代码(并将其保存在文件中),例如使用Mono.Cecil.
我还没有在C#4.0中测试它,但它应该工作.如果您想在C#3.5中尝试它,您还需要以下代码:
public static class Tuple
{
public static Tuple<T1, T2> Create<T1, T2>(T1 item1, T2 item2)
{
return new Tuple<T1, T2>(item1, item2);
}
public static Tuple<T1, T2, T3> Create<T1, T2, T3>(T1 item1, T2 item2, T3 item3)
{
return new Tuple<T1, T2, T3>(item1, item2, item3);
}
}
public class Tuple<T1, T2>
{
public Tuple(T1 item1, T2 item2)
{
Item1 = item1;
Item2 = item2;
}
public T1 Item1 { get; set;}
public T2 Item2 { get; set;}
public override string ToString()
{
return string.Format("Item1: {0}, Item2: {1}", Item1, Item2);
}
}
public class Tuple<T1, T2, T3> : Tuple<T1, T2>
{
public T3 Item3 { get; set; }
public Tuple(T1 item1, T2 item2, T3 item3) : base(item1, item2)
{
Item3 = item3;
}
public override string ToString()
{
return string.Format(base.ToString() + ", Item3: {0}", Item3);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
9975 次 |
| 最近记录: |