泛型 - 使用父类指定泛型类型

8

我正在研究一些协变性/逆变性的东西,我有一个更广泛的问题,但归根结底都是这个问题:

GenericRepository<BaseEntity> repo = new GenericRepository<ProductStyle>(context);

尽管BaseEntity是ProductStyle的父抽象类,但这并不起作用,有没有实现此目标的方法?


你得到了什么错误? - box86rowh
GenericRepository`1 是如何声明的? - O. R. Mapper
1
我猜测错误信息大概是“无法隐式转换类型 'UserQuery.GenericRepository<UserQuery.ProductStyle>' 为 'UserQuery.GenericRepository<UserQuery.BaseEntity>'”。 - David Hoerster
我能理解为什么需要协变性/逆变性,但是你试图做什么需要它们呢? - Vadim
基本上,我使用的泛型将按原样工作(我将它们存储在一个以对象为类型转换并使用通用GetRepo方法检索的列表中)。然而,为了添加操作,我需要传递一个特定的类型,而不仅仅是一个“对象”。我正在尝试创建一个框架,使得对对象执行的逻辑与对象本身分离。这将允许我通过传入的类型重载特定的对象(例如,可以将EmailAction附加到Product或Order以在保存时处理操作),以便根据类型以不同的方式处理操作。 - Daniel Dawes
由于GenericRepository<ProductStyle>没有GenericRepository<BaseEntity>作为(直接或间接)基类,因此上述声明是非法的。你需要的是协变性。但在C#中(至少到当前版本5.0),只有泛型接口和泛型委托类型可以被协变。像你所拥有的泛型类或泛型结构体则不行。因此,你能做到的最接近的方法是创建一个接口IGenericRepository<out TEntity>,其中out修饰符使得接口对TEntity具有协变性。然后你可以这样说:IGenericRepository<BaseEntity> repo = ...;(注意I)。 - Jeppe Stig Nielsen
2个回答

6
唯一的方法是使用out泛型限制(这将使保存对象变得困难,但检索它们很容易),在一个接口上(而不是)。 如果您有:
interface IGenericRepository<out T> {...}

假如一个 IGenericRepository<ProductStyle> 可以赋值给一个类型为 IGenericRepository<BaseEntity> 的变量,因为所有的 ProductStyle 也是 BaseEntity,而且我们限制了自己只使用协变 / out

IGenericRepository<BaseEntity> tmp = GetRepo<ProductStyle>(context);
// note that the right-hand-side returns IGenericRepository<ProductStyle>
...
private IGenericRepository<T> GetRepo(...) {...}

请注意,然而,这种协变的/ out 用法使得像以下这样的事情变得不可能:
interface IGenericRepository<out T>
{
    T Get(int id); // this is fine, but:
    void Save(T value); // does not compile; this is not covariantly valid
}

你太牛了!这正是我一直在努力寻找的解决方案,谢谢! - Daniel Dawes
2
@DanielDawes 只需确保您了解协变(或者反变)所施加的限制即可。 - Marc Gravell
1
不得不承认,我从未使用过 <out T> 泛型参数。我想不到那个。有趣。 - David Hoerster
使用Unit of Work模式与此一起使用,所以协变应该是没问题的,因为我正在使用上下文来管理通用类型的保存存储库。 - Daniel Dawes

0

我只是想知道类似这样的东西是否也有用——在定义GenericRepository时使用限制,限制T可以是哪种基础类型:

void Main()
{
    var repo = new GenericRepository<ProductStyle>(new ProductStyle());
    Console.WriteLine(repo.ToString());  //just output something to make sure it works...
}

// Define other methods and classes here
public class GenericRepository<T> where T : BaseEntity {
    private readonly T _inst;

    public GenericRepository(T inst){
        _inst = inst;
        _inst.DoSomething();
    }
}

public class BaseEntity {
    public Int32 Id {get;set;}

    public virtual void DoSomething() { Console.WriteLine("Hello"); }
}

public class ProductStyle : BaseEntity {
}

所以如果你有一个 GetRepo<T> 方法,该方法可以返回一个 T 类型的 GenericRepository,并且你可以确保 TBaseEntity 的子类。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接