我真的不明白这个协/逆变的事情......我不能同时拥有通用的 get 和 set 方法?
我想我会用一些例子来解释我的问题..
interface IModel {}
class MyModel : IModel {}
interface IRepo<T> where T: IModel {
}
class Repo : IRepo<MyModel> {
}
// Cannot implicitly convert.. An explicit convertion exists. Missing cast?
IRepo<IModel> repo = new Repo();
所以我需要协方差..
interface IRepo<out T> where T: IModel {
}
很好,它有效。然后我想使用它:
interface IRepo<out T> where T: IModel {
T ReturnSomething();
}
class Repo : IRepo<MyModel> {
public MyModel ReturnSomething() { return default(MyModel); }
}
一切都好,但是存储库也需要插入对象。使用 out 参数,我们不能这样做:
// Invalid variance: The type parameter 'T' must be contravariantly valid on 'IRepo<T>.InsertSomething(T)'. 'T' is covariant.
interface IRepo<out T> where T: IModel {
T ReturnSomething();
void InsertSomething(T thing);
}
class Repo : IRepo<MyModel> {
public MyModel ReturnSomething() { return default(MyModel); }
public void InsertSomething(MyModel thing) { }
}
所以我尝试添加两个参数:
interface IRepo<out TReturn, TInsert>
where TReturn : IModel
where TInsert : IModel
{
TReturn ReturnSomething();
void InsertSomething(TInsert thing);
}
我得到了与第一个示例相同的错误。在 TInsert 中使用 时,我遇到了同样的错误
那么我到底如何支持插入和获取呢?
编辑:所以我找到了一个可能的解决方案,但它与最佳解决方案相去甚远
interface IRepo<out TResult> where TResult : IModel {
TResult ReturnSomething();
// I need to duplicate my constraint here..
void InsertSomething<TInsert>(TInsert thing) where TInsert : IModel;
}
class Repo : IRepo<MyModel> {
public MyModel ReturnSomething() { return default(MyModel); }
// ... And here
public void InsertSomething<T>(T thing) where T: IModel { }
}
编辑2:回应埃里克:这是一个更完整的示例我想要实现的目标。 我真的很想要协方差,这样我就可以对 IRepo 实例进行分组,并且我仍然希望它们具有使用模型作为实例的添加/更新方法。我知道我无法获得添加项目的编译时类型安全性,但对于这个用例,我只需要读取元素。
interface IModel { }
class SomeModel : IModel { }
class OtherModel : IModel { }
interface IRepo<T>
{
T ReturnSomething();
void AddSomething(T thing);
}
interface ISubRepo<T> : IRepo<T> where T : IModel { }
class SomeSubRepo : ISubRepo<SomeModel> {
public SomeModel ReturnSomething() { return default(SomeModel); }
public void AddSomething(SomeModel thing) { }
}
class OtherSubRepo : ISubRepo<OtherModel> {
public OtherModel ReturnSomething() { return default(OtherModel); }
public void AddSomething(OtherModel thing) { }
}
class Program {
static void Main(string[] args)
{
ISubRepo<IModel>[] everyone = new ISubRepo<IModel>[] {
new SomeSubRepo(),
new OtherSubRepo()
};
WorkOnAll(everyone);
}
static void WorkOnAll(IEnumerable<ISubRepo<IModel>> everyone)
{
foreach(ISubRepo<IModel> repo in everyone) {
IModel model = repo.ReturnSomething();
// Etc.
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我认为最好的选择是将界面一分为二:
现在您可以创建一个包含
Repo
实例的List>
。I think that your best bet is to split your interface in two:
Now you can create a
List<IReadableRepo<IModel>>
which containsRepo
instances.如果要插入和返回相同类型的对象,则需要不变性。您只需在声明变量时牢记这一点即可。
只需使用该行的第一个片段来声明 repo 变量:
编辑:我花了 10 分钟来编写必要的代码。我可以向您保证,这段代码可以在我的计算机上编译(在 Visual C# Express 上):
If you want to insert and return the same type of object, you need invariance. You just have to declare your variable with that in mind.
Just use your first snippet with that line to declare the repo variable:
Edit: I have taken the necessary 10 minutes to write the code necessary. I can assure you that that bit of code compiles on my computer (on Visual C# Express):