我正在从源中选择一些数据,我想将其传递给处理数据的通用方法。我选择的每个属性都需要一些与处理相关的元数据。这大致是我希望能够做到的:
var items = from i in db.bar
select new {
a = i.Prop1.ToString(),
b = i.Prop2.ToString(),
c = Prop3
};
// Roughly what I would like to be able to do:
ProcessData(items,
new DataSelector() { Header = "head1", Selector = (d => d.Prop1) },
new DataSelector() { Header = "head2", Selector = (d => d.Prop2) });
我能得到的最接近的是:
private class DataSelector<T> {
public string Header { get; set; }
public Func<T, string> Selector { get; set; }
}
private static void Process<T>(IEnumerable<T> stuff,
params ColumnDef<T>[] defs) {
foreach (var item in stuff) {
// Use all defs like...
Console.WriteLine(defs[0].Header + ": " + defs[0].Selector(item));
}
}
Process(items,
new DataSelector<dynamic>() { Header = "head1", Selector = (d => d.Prop1) });
但是这允许我编写(d => d.NonExistantProp)
并且它将在运行时编译并失败(boo,hiss)。我也可以为单个属性执行此操作并保持类型安全性,因为类型似乎是隐式计算出来的,但这不会让我传递参数集:
private static void Process<T>(IEnumerable<T> stuff,
string header,
Func<T, string> selector) {
// stuff happens
}
Process(items, "head1", (d => d.Prop1));
那么......有没有办法维护选择器的编译时类型安全性以及相关数据组?
脚注:我感谢我可以拥有Process<T>(IEnumerable<T> items, List<string> headers, params Func<T, string> selectors[])
但如果标头未与选择器配对,也会产生运行时错误。
答案 0 :(得分:1)
您需要的是创建基于给定序列(或项目或使用该类型的任何其他内容)的类型推断的DataSelector
的某种方式。
我们可以创建一个非通用的DataSelector对应方,它可以有一个静态Create
方法,可以用这种方式编写,以便允许类型推断:
private class DataSelector
{
public static DataSelector<T> Create<T>(IEnumerable<T> sequence,
string header, Func<T, string> selector)
{
return new DataSelector<T>() { Header = header, Selector = selector };
}
}
现在来电者可以写:
Process(items, DataSelector.Create(items, "first", item => item.Prop1));
我们现在推断数据选择器的T
类型,不需要指定没有名称的类型。
请注意,我们实际上并没有在此Create
方法中迭代序列;它根本没用过;它基本上是一个虚拟参数。
这是我们在.NET框架中的许多地方使用的模式,例如Tuple
。
答案 1 :(得分:1)
您可以使用Tuple<T1,T2,T3>
代替匿名类:
var items = db.bar.AsEnumerable().Select(c=>Tuple.Create(i.Prop1, i.Prop2, i.Prop3));
ProcessData(items,
new DataSelector<Tuple<T1,T2,T3>>("head1", d => d.Prop1.ToString()),
new DataSelector<Tuple<T1,T2,T3>>("head2", d => d.Prop2.ToString()));
private class DataSelector<T>
{
public DataSelector(string header, Func<T, string> selector)
{
Header = header;
Selector = selector;
}
public string Header { get; set; }
public Func<T, string> Selector { get; set; }
}
private static void Process<T>(IEnumerable<T> stuff,
params ColumnDef<T>[] defs)
{
foreach (var item in stuff) {
// Use all defs like...
Console.WriteLine(defs[0].Header + ": " + defs[0].Selector(item));
}
}