【发布时间】:2012-08-31 13:35:49
【问题描述】:
我正在实现一个流畅的构建器模式,它需要在静态扩展方法中接受枚举并迭代其内容,同时将仿函数应用于枚举的内容。如(不是实际代码,只是一个说明):
public static IValidator<IEnumerable<T>> Each<T>(
this IValidator<IEnumerable<T>> enumerable,
Func<T, bool> action)
{
foreach (T value in enumerable)
action(value);
return validator;
}
这对于可枚举对象非常有效,但对于继承的类型/接口却失败了。比方说:
IValidator<IEnumerable<Guid>> validator = ...;
IEnumerable<Guid> guids = ...;
validator.Each(guids, guid => guid != Guid.Empty); // ok
IList<Guid> guids = ...;
validator.Each(guids, guid => guid != Guid.Empty); // doesn't compile (see below)
例外是:
IValidator<IList<Guid>>不包含“每个”的定义 并且没有扩展方法“每个”接受类型的第一个参数IValidator<IList<Guid>>可以找到(您是否缺少使用 指令还是程序集引用?
我的问题是关于IValidator<T> 的继承链,更具体地说,是它的泛型类型参数T。为什么类型 IValidator<IEnumerable<T>> 不能从 IValidator<IList<T>> 分配?我想不出在哪个IList<T> 不是IEnumerable<T> 的情况下(给定相同的T)。
将泛型参数限制为 T : IEnumerable<R> 确实有效,但这需要两个类型参数(T 和 R),如果可能的话,我想避免。
有什么想法吗?更好的解决方案?谢谢。
【问题讨论】:
-
我认为这可能是一个协变和逆变问题。让我看看我能不能得到一些示例代码来看看是不是这样。
-
即使你解决了你当前的问题,方法的意义何在?您没有使用传入的函子的布尔结果,因此除非函子改变对象,否则最终结果不会发生任何变化,如果它确实改变了它,那么......嗯......它不应该,因为 Linq 是专为序列查询而非突变而设计。
-
@Servy - 这只是说明我意图的示例,而不是实际代码。我将其保留为
Func<T, bool>,因为在下面的示例中编写调用 lambda 所需的代码比Action<T>少。我有点懒惰。 :-) -
IEnumerable(.Net 4+)是协变的,但 IList 不是,因为后者同时接受输入和输出参数。
-
好吧,我没有时间写一些代码。我的建议是当你使用
IValidator<T>时尝试IValidator<out T>或IValidator<in T>,我忘了是哪一个。您也可以只使用var guids = validator.Each.....这可能会有所帮助,但它不会消除您的设计问题。
标签: c# .net generics extension-methods ienumerable