我可以使用linq结果中的匿名类型来创建一个类集合作为方法参数的一部分吗?

时间:2013-11-19 17:51:20

标签: c# linq

我正在从源中选择一些数据,我想将其传递给处理数据的通用方法。我选择的每个属性都需要一些与处理相关的元数据。这大致是我希望能够做到的:

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[])但如果标头未与选择器配对,也会产生运行时错误。

2 个答案:

答案 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));
    }
}