我正在实现一个fluent builder模式,它要求在静态扩展方法中接受可枚举项并迭代其内容,同时对可枚举项的内容应用函子。如中所示(不是实际的代码,只是一个示例):
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>>
不包含“each”的定义并且没有扩展方法“each”接受类型为的第一个参数
IValidator<IList<Guid>>
可以找到(您是否缺少指令还是程序集引用?
我的问题是关于
IValidator<T>
的继承链,更具体地说,它的泛型类型参数T
。为什么类型IValidator<IEnumerable<T>>
不能从IValidator<IList<T>>
分配?我想不出在什么情况下IList<T>
不是IEnumerable<T>
(同样的T
)。将泛型参数约束为
T : IEnumerable<R>
确实有效,但这需要两个类型参数(T
和R
),如果可能的话,我希望避免这两个类型参数。有什么想法吗?更好的解决方案?谢谢。
最佳答案
这是因为IValidator<T>
接口的定义。我敢打赌是这样的:
public interface IValidator<T>
你真正想要的是:
public interface IValidator<out T>
这将使您的接口covariant,这意味着您可以将
IValidator<T2>
的实现分配给IValidator<T>
,假设T2
来自T
。在本例中,
IList<T>
从IEnumerable<T>
派生,因此您应该能够将T
声明为协变。但是,这取决于IValidator<T>
上的方法以及它们是如何暴露的。也就是说,如果
IValidator<T>
上的方法将T
in的实例作为接口上任何方法的参数,那么就不能将接口声明为协变的。如果是这样的话,那么您应该能够理解
Each
的定义:public static IValidator<T> Each<T, TValue>(
this IValidator<T> enumerable,
Func<TValue, bool> action) where T : IEnumerable<TValue>
{
foreach (TValue value in enumerable)
action(value);
return validator;
}
这将表明
T
应该从IEnumerable<TValue>
派生。