How can I return <TEnumerable, T> : where TEnumerable:IEnumerable<T>

白昼怎懂夜的黑 提交于 2019-12-05 07:28:24

Type inference in C# is very complicated - just for once, I'm not going to get the spec out to try to step through it, because I'm aware of just how horrible it can become.

I believe the problem is that neither of the parameter/argument combinations gives the compiler enough information to infer T:

  • The TEnumerable items parameter doesn't mention T, so it isn't used to infer T, despite the type constraint
  • The Action<T> parameter would be fine, but the compiler can't make an inference based on the lambda expression you're providing

I can't think of a good change to the method signature that would make exactly your first code work - but you can change how you invoke the method just a little to make it work, by specifying the parameter type in the lambda expression:

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

The downside of that is that it won't work with anonymous types, of course.

One workaround for that downside is a pretty horrible one, but it lets you express the type of T via a parameter instead, when you need to. You change the method signature to:

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

If you wanted to call the method with a list of some anonymous type, you could write:

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

... where the final argument would match the anonymous type. That will allow the type of T to be inferred by the final parameter instead of the second one. You can use that for other types of course, but I'd probably stick to using it for anonymous types instead.

That's all a pretty horrible hack, and I don't think I'd actually use it, but if you really, really need this to work with anonymous types, it would cope.

Declare your extension using just T, like so:

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

This has the downside of losing the specific sub-class of IEnumerable that you implement.

It's easy to implement overloads for the specific subclasses you care about:

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

Returning the IEnumerable after iterating it is a bit scary, though. IEnumerables might not be restartable.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!