T 必须逆变有效

发布于 2024-10-17 22:58:54 字数 295 浏览 4 评论 0原文

这有什么问题吗?

interface IRepository<out T> where T : IBusinessEntity
{
    IQueryable<T> GetAll();
    void Save(T t);
    void Delete(T t);
}

它说:

无效方差:类型参数“T”必须是逆变有效的 在“MyNamespace.IRepository.Delete(T)”上。 “T”是协变的。

What is wrong with this?

interface IRepository<out T> where T : IBusinessEntity
{
    IQueryable<T> GetAll();
    void Save(T t);
    void Delete(T t);
}

It says:

Invalid variance: The type parameter 'T' must be contravariantly valid
on 'MyNamespace.IRepository.Delete(T)'. 'T' is covariant.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

乖乖兔^ω^ 2024-10-24 22:58:54

考虑一下如果编译器允许这样做会发生什么:

interface IR<out T>
{
    void D(T t);
}

class C : IR<Mammal>
{
    public void D(Mammal m)
    {
        m.GrowHair();
    }
}
...
IR<Animal> x = new C(); 
// legal because T is covariant and Mammal is convertible to Animal
x.D(new Fish()); // legal because IR<Animal>.D takes an Animal

而您只是试图在鱼上长出毛发。

“out”表示“T 仅用于输出位置”。您正在输入位置使用它。

Consider what would happen if the compiler allowed that:

interface IR<out T>
{
    void D(T t);
}

class C : IR<Mammal>
{
    public void D(Mammal m)
    {
        m.GrowHair();
    }
}
...
IR<Animal> x = new C(); 
// legal because T is covariant and Mammal is convertible to Animal
x.D(new Fish()); // legal because IR<Animal>.D takes an Animal

And you just tried to grow hair on a fish.

The "out" means "T is only used in output positions". You are using it in an input position.

无力看清 2024-10-24 22:58:54

您只能以协变方式(即在返回类型中)使用 out 类型参数。因此,IQueryableGetAll() 是正确的,但 void Delete(T t) 不正确。

由于 T 在您的类中同时使用协变和逆变,因此您不能在此处使用 out(也不能使用 in)。

如果您想了解更多关于这背后的理论背景,请稍事休息并阅读“协方差”和逆变”维基百科文章


欢迎回来。那么,如果您需要存储库中的所有这些方法但仍然需要协变接口,您该怎么办?您可以将协变部分提取到自己的接口中:

interface IDataSource<out T> where T : IBusinessEntity
{
    IQueryable<T> GetAll();
}

interface IRepository<T> : IDataSource<T> where T : IBusinessEntity
{
    void Save(T t);
    void Delete(T t);
}

这也是 .NET BCL 解决此问题的方式:IEnumerable 是协变的,但仅支持“读操作”。 ICollectionIEnumerable 的子类型,允许读取和写入操作,因此本身不能是协变的。

You can use an out type parameter only covariantly, i.e., in the return type. Therefore, IQueryable<T> GetAll() is correct, but void Delete(T t) is not.

Since T is used both co- and contravariantly in your class, you cannot use out here (nor in).

If you want to know more about the theoretical background behind this, take a quick break and read the "Covariance and Contravariance" Wikipedia article.


Welcome back. So, what do you do if you need all those methods in your repository but still need a covariant interface? You can extract the covariant part into its own interface:

interface IDataSource<out T> where T : IBusinessEntity
{
    IQueryable<T> GetAll();
}

interface IRepository<T> : IDataSource<T> where T : IBusinessEntity
{
    void Save(T t);
    void Delete(T t);
}

This is also how the .NET BCL solves this issue: IEnumerable<out T> is covariant, but only supports "read operations". ICollection<T> is a subtype of IEnumerable<out T>, allows read and write operations, and, thus, cannot be covariant itself.

思念绕指尖 2024-10-24 22:58:54

以下两种方法是错误的:

void Save(T t);
void Delete(T t);

不能将 T 作为方法参数。仅当您希望它在泛型定义中是协变 (out T) 时才作为返回类型。

或者,如果您想要逆变,那么您可以仅使用泛型参数作为方法参数,而不使用返回类型:

interface IRepository<in T> where T : IBusinessEntity
{
    void Save(T t);
    void Delete(T t);
}

The following two methods are wrong:

void Save(T t);
void Delete(T t);

You can't have T as method argument. Only as return type if you want it to be covariant (out T) in your generic definition.

Or if you want contravariance then you could use the generic parameter only as method argument and not return type:

interface IRepository<in T> where T : IBusinessEntity
{
    void Save(T t);
    void Delete(T t);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文