IEnumerable 的转换对于扩展方法问题
我有以下类和扩展类(对于本例):
public class Person<T>
{
public T Value { get; set; }
}
public static class PersonExt
{
public static void Process<TResult>(this Person<IEnumerable<TResult>> p)
{
// Do something with .Any().
Console.WriteLine(p.Value.Any());
}
}
我期望我可以编写类似以下内容的内容并且它会起作用,但它不起作用:
var x = new Person<List<String>>();
x.Process();
由于 List 在继承树中比 IEnumerable 更低,所以不应该这样有效吗?当然,如果我新建一个 Person
,它就会起作用,因为那是直接类型。
我正在尝试使用一种扩展方法,只要 T 实现 IEnumerable
,就可以应用于所有 Person
,因为我需要使用.Any()
方法。
编辑:也许我对协方差的理解不正确?我知道 IEnumerable
应该转换为 IEnumerable
EDIT2:忘记提及我正在使用 .net 4.0。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
IList
可以转换为IEnumerable
。问题是您试图将Person
转换为>
Person>
,这是非法的。例如,这样写是完全有效的:因为 Value 的类型是
IEnumerable
,而字符串数组是IEnumerable
。但是,您不能编写:因为 Value 的类型为
List
。由于您不能在所有可以使用Person>
的地方使用Person
,因此它不是合法的投掷。>
请注意,如果您向扩展方法添加第二个类型参数,您可以执行类似于您想要的操作:
不幸的是,编译器将无法推断这两个类型参数,因此您必须像这样调用它:
如果您使用 C# 4.0 并且可以使用协变,那么您可以为 person 定义一个协变接口:
然后将您的扩展方法编写为:
由于
IPerson.Value
被读取-仅,IPerson
可以在>
IPerson>
可以使用的任何地方使用,并且转换有效。IList<String>
can convert toIEnumerable<String>
. The problem is that you're trying to convertPerson<List<String>>
toPerson<IEnumerable<String>>
, which is illegal. For example, it's perfectly valid to write:since Value is of type
IEnumerable<String>
and a string array is anIEnumerable<String>
. However, you cannot write:since Value is of type
List<String>
. Since you can't use aPerson<List<String>>
in all places where you could use aPerson<IEnumerable<String>>
, it's not a legal cast.Note that you can do something similar to what you want if you add a second type parameter to your extension method:
Unfortunately, the compiler won't be able to infer both type parameters, so you would have to call it like this:
If you are using C# 4.0 and can use covariance, then you can define a covariant interface for person:
And then write your extension method as:
Since
IPerson<T>.Value
is read-only, aIPerson<List<String>>
can be used everywhere that anIPerson<IEnumerable<String>>
can be, and the conversion is valid.我不确定您是否已经完全掌握了泛型的正确用法。无论如何......
唯一不正确的是你的扩展方法的声明,以及你试图约束扩展方法的方式。
我真正所做的就是将
Person
重命名为Thing
,这样我们就不会纠结于Person
>确实是。
如果更适用,您还可以将通用约束移至
Thing
。I'm not sure you've quite grasped the correct use of generics. In any event ...
The only thing that is incorrect is your declaration of extension method, and the way you are attempting to constrain the extension method.
All I've really done is rename
Person
toThing
so that we're not getting hung up on what aPerson<List<string>>
really is.You could also move the generic constraint to the
Thing<T>
if that was more applicable.您提到了协方差,但实际上并没有使用它。您必须在通用参数上指定
in
或out
。请注意,协/逆变不适用于类类型;它们必须应用于接口。因此,引入一个接口并使其协变:
允许此代码编译:
You mention covariance, but don't actually use it. You have to specify
in
orout
on your generic parameters. Note that co/contravariance doesn't work on class types; they must be applied to interfaces.So, introducing an interface and making it covariant:
allows this code to compile: