我真的不明白这个协/逆变的事情......我不能同时拥有通用的 get 和 set 方法?

发布于 2024-10-02 05:27:04 字数 3139 浏览 2 评论 0 原文

我想我会用一些例子来解释我的问题..

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.
        }
    }
}

I think I'll explain my problems with some examples..

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();

So I need covariance..

interface IRepo<out T> where T: IModel {
}

Nice, it works. Then I want to use it:

interface IRepo<out T> where T: IModel {
    T ReturnSomething();
}

class Repo : IRepo<MyModel> {
    public MyModel ReturnSomething() { return default(MyModel); }
}

All good, but the repo needs to insert objects too. With an out parameter, we cannot do this:

// 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) { }
}

So I try to add two parameters:

interface IRepo<out TReturn, TInsert>
    where TReturn : IModel
    where TInsert : IModel
{
    TReturn ReturnSomething();
    void InsertSomething(TInsert thing);
}

And I get the same error as in the very first example. I get the same error when using in TInsert

So how on earth could I support both insertion and fetching?

EDIT: So I've found a possible solution, but it's far from optimal

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 { }
}

EDIT2: In response to Eric: This is a more complete example of what I'm trying to achieve.
I really would like covariance so I can group the IRepo instances, and I still want them to have add/update methods using the model as instance. I understand I cannot get compile-time type safety for adding items, but for this use case, I just need to read the elements.

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 技术交流群。

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

发布评论

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

评论(2

巨坚强 2024-10-09 05:27:04

我认为最好的选择是将界面一分为二:

    interface IReadableRepo<out T> where T : IModel
    {
        T ReturnSomething();
    }

    interface IWritableRepo<in T> where T : IModel
    {
        void InsertSomething(T thing);
    }

    class Repo : IReadableRepo<MyModel>, IWritableRepo<MyModel>
    {
        ...
    }

现在您可以创建一个包含 Repo 实例的 List>

I think that your best bet is to split your interface in two:

    interface IReadableRepo<out T> where T : IModel
    {
        T ReturnSomething();
    }

    interface IWritableRepo<in T> where T : IModel
    {
        void InsertSomething(T thing);
    }

    class Repo : IReadableRepo<MyModel>, IWritableRepo<MyModel>
    {
        ...
    }

Now you can create a List<IReadableRepo<IModel>> which contains Repo instances.

楠木可依 2024-10-09 05:27:04

如果要插入和返回相同类型的对象,则需要不变性。您只需在声明变量时牢记这一点即可。

只需使用该行的第一个片段来声明 repo 变量:

IRepo<MyModel> repo = new Repo();

编辑:我花了 10 分钟来编写必要的代码。我可以向您保证,这段代码可以在我的计算机上编译(在 Visual C# Express 上):

public interface IModel {

}

public interface IRepo<T> where T : IModel {
    T returnModel();
    void putModel(T model);
}

public class MyModel : IModel {

}

public class Repo : IRepo<MyModel> {
}

public static class Program {
    void main() {
        IRepo<MyModel> repo = new Repo();
        var model = new MyModel();
        repo.putModel(model);
        var model2 = repo.returnModel();
    }
}

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:

IRepo<MyModel> repo = new Repo();

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):

public interface IModel {

}

public interface IRepo<T> where T : IModel {
    T returnModel();
    void putModel(T model);
}

public class MyModel : IModel {

}

public class Repo : IRepo<MyModel> {
}

public static class Program {
    void main() {
        IRepo<MyModel> repo = new Repo();
        var model = new MyModel();
        repo.putModel(model);
        var model2 = repo.returnModel();
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文