我怎样才能 return <TEnumerable, T> : where TEnumerable:IEnumerable<T>
How can I return <TEnumerable, T> : where TEnumerable:IEnumerable<T>
目标:泛型枚举类型与 returned 时的类型相同。
注意:这在输入类型时有效,但我不明白为什么不能推断它们。
List<T>
然后 return List<T>
IOrderedEnumerable<T>
然后 return IOrderedEnumerable<T>
等
当前方法(只有输入所有类型才有效)
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));
我缺少的是为什么 C# 无法推断类型,尽管 where
过滤器确实使类型准确。我理解您为什么要为方法提供全部或 none 泛型类型,因此请不要向我指出这些答案。
编辑:如果我无法推断类型;那我怎样才能让它更优雅?
仅使用 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 的特定子class。
很容易为您关心的特定子class实现重载:
public static IOrderedEnumerable<T> WithEach<T>(this IOrderedEnumerable<T> items, Action<T> action)
{
((IEnumerable<T>)items).WithEach(action);
return items;
}
不过,迭代后返回 IEnumerable 有点可怕。
IEnumerables 可能无法重新启动。
C# 中的类型推断 非常 复杂 - 就这一次,我不会拿出规范来尝试逐步完成它,因为我知道它会变得多么可怕。
我相信问题是parameter/argument组合都没有给编译器足够的信息来推断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
的类型由最后一个参数而不是第二个参数推断出来。您当然可以将其用于其他类型,但我可能会坚持将其用于匿名类型。
这是一个非常可怕的 hack,我不认为我 实际上 使用它,但如果你真的,真的需要它来处理匿名类型,它会应付的。
目标:泛型枚举类型与 returned 时的类型相同。
注意:这在输入类型时有效,但我不明白为什么不能推断它们。
List<T>
然后 return List<T>
IOrderedEnumerable<T>
然后 return IOrderedEnumerable<T>
等
当前方法(只有输入所有类型才有效)
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));
我缺少的是为什么 C# 无法推断类型,尽管 where
过滤器确实使类型准确。我理解您为什么要为方法提供全部或 none 泛型类型,因此请不要向我指出这些答案。
编辑:如果我无法推断类型;那我怎样才能让它更优雅?
仅使用 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 的特定子class。
很容易为您关心的特定子class实现重载:
public static IOrderedEnumerable<T> WithEach<T>(this IOrderedEnumerable<T> items, Action<T> action)
{
((IEnumerable<T>)items).WithEach(action);
return items;
}
不过,迭代后返回 IEnumerable 有点可怕。 IEnumerables 可能无法重新启动。
C# 中的类型推断 非常 复杂 - 就这一次,我不会拿出规范来尝试逐步完成它,因为我知道它会变得多么可怕。
我相信问题是parameter/argument组合都没有给编译器足够的信息来推断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
的类型由最后一个参数而不是第二个参数推断出来。您当然可以将其用于其他类型,但我可能会坚持将其用于匿名类型。
这是一个非常可怕的 hack,我不认为我 实际上 使用它,但如果你真的,真的需要它来处理匿名类型,它会应付的。