如何返回<tenumerable,t =“”>:TEnumerable:IEnumerable <t>

时间:2018-12-04 19:48:07

标签: c# extension-methods generic-collections

目标:通用枚举类型在返回时是相同类型。

注意:输入类型时此方法有效,但我不明白为什么不能推断出它们。

List<T>然后返回List<T>

IOrderedEnumerable<T>然后返回IOrderedEnumerable<T>

ETC

当前方法(仅在输入所有类型时有效)

public static TEnumerable WithEach<TEnumerable, T>(this TEnumerable items, Action<T> action)
where TEnumerable : IEnumerable<T>
{
    foreach (var item in items) action.Invoke(item);
    return items;
}

仅示例

var list = new List<int>(); //TODO: Mock random values
list.WithEach(x => Console.WriteLine(x)) //Here WithEach ideally returns List<int> following orignal type List<int>
    .OrderBy(x => x) 
    .WithEach(x => Console.WriteLine(x)); //Here WithEach ideally returns IOrderedEnumerable<int> following OrderBy

使其正常工作

var list = new List<int>(); //TODO: Mock random values
list.WithEach<List<int>, int>(x => Console.WriteLine(x))
    .OrderBy(x => x) 
    .WithEach<IOrderedEnumerable<int>, int>(x => Console.WriteLine(x));

我所缺少的是,尽管where过滤器确实使类型准确,但是C#不能推断类型。我了解您为什么不向方法提供全部或不提供泛型类型,所以请不要将我指向这些答案。

编辑:如果我不能推断类型;那我怎样才能使它更优雅呢?

2 个答案:

答案 0 :(得分:9)

C#中的

类型推断非常复杂-仅一次,我不会制定规范尝试逐步进行,因为我知道它可能会多么可怕成为。

相信问题是,参数/参数组合都不给编译器足够的信息来推断T

  • TEnumerable items参数未提及T,因此尽管有类型约束,它也不能用于推断T
  • Action<T>参数很好,但是编译器无法根据您提供的lambda表达式进行推断

我想不出对方法签名的一个很好的改变,以使完全可以正常工作,但是您可以稍微改变一下方法的调用方式通过在lambda表达式中指定参数类型来使其工作:

var list = new List<int>();
list.WithEach((int x) => Console.WriteLine(x++))
    .OrderBy(x => x) 
    .WithEach((int x) => Console.WriteLine(x));

缺点是,它当然不适用于匿名类型。

该缺点的一种解决方法非常可怕,但是它使您可以在需要时通过参数来表示T的类型。您将方法签名更改为:

public static TEnumerable WithEach<TEnumerable, T>(
    this TEnumerable items,
    Action<T> action,
    T ignored = default(T))

如果您想使用某种匿名类型的列表来调用该方法,则可以编写:

list.WithEach(x => Console.WriteLine(x.Name), new { Name = "", Value = 10 });

...,其中最终参数与匿名类型匹配。这样可以通过最终参数而不是第二个参数来推断T的类型。当然,您可以将其用于其他类型,但我可能会坚持将其用于匿名类型。

这都是一个非常可怕的骇客,我不认为我会实际使用它,但是如果您确实需要使用匿名类型,它将可以应对。

>

答案 1 :(得分:-1)

仅使用T声明扩展名,就像这样:

public static IEnumerable<T> WithEach<T>(this IEnumerable<T> items,Action<T> action)
{
    foreach (var item in items) action.Invoke(item);
    return items;
}

这具有丢失您实现的IEnumerable特定子类的缺点。

很容易为您关心的特定子类实现重载:

public static IOrderedEnumerable<T> WithEach<T>(this IOrderedEnumerable<T> items, Action<T> action)
{
    ((IEnumerable<T>)items).WithEach(action);
    return items;
} 

但是,在迭代之后返回IEnumerable有点吓人。 IEnumerables可能无法重新启动。