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

时间:2010-11-09 14:34:11

标签: inheritance interface c#-4.0 covariance contravariance

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

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

我得到了与第一个例子中相同的错误。使用in 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 { }
}

EDIT2 :回应Eric:这是我想要实现的更完整的例子。 我真的想要协方差,所以我可以对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.
        }
    }
}

2 个答案:

答案 0 :(得分:2)

如果要插入并返回相同类型的对象,则需要不变性。您只需要记住您的变量即可。

只需使用该行的第一个片段来声明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();
    }
}

答案 1 :(得分:2)

我认为您最好的选择是将界面分成两部分:

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

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