代码之家  ›  专栏  ›  技术社区  ›  simendsjo

我真的不明白这个协/反方差的东西…我不能同时有通用的get和set方法?

  •  9
  • simendsjo  · 技术社区  · 14 年前

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

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

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

    编辑2 :作为对埃里克的回应:这是一个更完整的例子,说明我正在努力实现的目标。 我真的很想使用协方差,这样我就可以对IRepo实例进行分组,而且我仍然希望它们具有使用模型作为实例的add/update方法。我知道我无法获得添加项的编译时类型安全性,但是对于这个用例,我只需要阅读元素。

    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 回复  |  直到 14 年前
        1
  •  2
  •   kvb    14 年前

    我认为你最好的办法是把你的界面一分为二:

        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

        2
  •  2
  •   Jean Hominal    14 年前

    只需将第一个代码片段与该行一起声明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();
        }
    }