gpt4 book ai didi

c# - .Net 4 : Easy way to dynamically create List> 结果

转载 作者:太空狗 更新时间:2023-10-29 22:24:59 24 4
gpt4 key购买 nike

对于远程处理场景,结果最好以数组或元组对象列表的形式接收(其中的好处是强类型)。

示例:动态转换 SELECT Name, Age FROM Table => List<Tuple<string,int>>

问题:是否有任何示例可以在给定任意数据表(如 SQL 结果集或 CSV 文件)且每列的类型仅在运行时已知的情况下生成可动态创建强类型 List<Tuple<...>> 的代码目的。代码应该是动态生成的,否则会非常慢。

最佳答案

编辑:我更改了代码以使用 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;
}
}

如果您想使用 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);
}
}

关于c# - .Net 4 : Easy way to dynamically create List<Tuple<. ..>> 结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2009392/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com