【问题标题】:Covariance in generic interfaces泛型接口中的协方差
【发布时间】:2015-04-05 08:45:16
【问题描述】:

我想创建一个可排序的 observableCollection 所以我开始创建一个继承 observable 的类,并使用一些方法对其进行排序,然后我希望该类将索引保存到子类中,所以我创建了一个接口,该接口公开了一个我可以写入的索引属性,并且我约束了 T我的集合类是我的接口,然后我希望能够从每个项目访问 parentCollection 并且这里问题开始了,因为父集合的类型是通用的...... 我尝试了很多解决方案,我认为协方差或不变性是方法,但我无法让它工作......

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ClassLibrary1
{
    public class SortableCollection<T> : System.Collections.ObjectModel.ObservableCollection<T>, ISortableCollection<T> where T : ISortable<T>
    {
        public void Sort()
        {
            //We all know how to sort something
            throw new NotImplementedException();
        }

        protected override void InsertItem(int index, T item)
        {
            item.Index = index;
            item.ParentCollection = this;
            base.InsertItem(index, item);
        }
    }

    public interface ISortableCollection<T> : IList<T>
    {
        void Sort();
    }

    public interface ISortable<T>
    {
        Int32 Index { get; set; }
        ISortableCollection<T> ParentCollection { get; set; }
    }

    public class BaseClass : ISortable<BaseClass>
    {
        public int Index { get; set; }

        public ISortableCollection<BaseClass> ParentCollection { get; set; }
    }

    public class DerivedClass : BaseClass { }

    public class Controller
    {
        SortableCollection<BaseClass> MyBaseSortableList = new SortableCollection<BaseClass>();
        SortableCollection<DerivedClass> MyDerivedSortableList = new SortableCollection<DerivedClass>();

        public Controller()
        {
            //do things
        }
    }
}

这或多或少是设置。 我希望能够创建一个SortableCollection&lt;DerivedClass&gt;,但类型不匹配......这是正确的做法吗?

确切的错误是

错误 1 ​​类型“ClassLibrary1.DerivedClass”不能用作泛型类型或方法'ClassLibrary1.SortableCollection&lt;T&gt;' 中的类型参数“T”。没有从“ClassLibrary1.DerivedClass”到'ClassLibrary1.ISortable&lt;ClassLibrary1.DerivedClass&gt;' 的隐式引用转换。 c:\users\luigi.trabacchin\documents\visual studio 2013\Projects\ClassLibrary1\ClassLibrary1\Class1.cs 48 89 ClassLibrary1

【问题讨论】:

    标签: c# .net generics variance


    【解决方案1】:

    问题是您对T 的约束是“T 必须是I&lt;T&gt;”,并且您为T 传递了DerivedClass,但DerivedClass 不能转换为@ 987654328@,可转换为I&lt;BaseClass&gt;

    我不知道你想用TI&lt;T&gt; 的约束来表示什么。我知道人们经常使用这种模式来尝试表示 C# 类型系统实际上并未实现的约束。有关详细信息,请参阅我关于该主题的文章:

    http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx

    我鼓励您大大简化事情;您似乎试图在类型系统中捕获太多内容。

    之所以I&lt;D&gt;不能转换成I&lt;B&gt;是因为要让variance生效,接口必须标记为support variance;将T 标记为outin,具体取决于您需要协变还是逆变。

    但是,由于IList&lt;T&gt; 是不变的,因此使派生接口协变或逆变是不合法的。请考虑使用IEnumerable&lt;T&gt;,因为它在T 中是协变的。

    为了使接口在T 中协变,它只需要在输出 位置使用TList&lt;T&gt; 在输入和输出位置都使用T,因此它不能是协变或逆变的。

    【讨论】:

    • 我玩弄了这段代码,但无法解决方差问题,并且 IList 基类被删除,因为 ParentCollection 既进又出。
    • 嘿,感谢 Eric 提供了非常好的答案。我想要实现的是还能够让当前项目的兄弟姐妹切换它们的顺序,然后调用排序...我想我会检查父集合的类型并在父集合实现时调用该方法界面......我使用约束接近了,但它真的感觉很尴尬并且仍然缺少一些东西。
    【解决方案2】:

    您需要DerivedClass 成为ISortable&lt;DerivedClass&gt;

    public class DerivedClass : BaseClass, ISortable<DerivedClass>
    {
        public new ISortableCollection<DerivedClass> ParentCollection
        {
            get { throw new NotImplementedException(); }
            set { throw new NotImplementedException(); }
        }
    }
    

    T 上的协变和逆变不能在这里起作用,因为您是从不变的IList&lt;T&gt; 派生的。

    即使删除 IList&lt;T&gt; 并删除 getter,我现在也无法让它在有差异的情况下正常工作。不完全是我的强项。这是类型系统的一部分,如果你能提供帮助,最好不要管它。

    如果类型系统让您头疼,请考虑动态解决方案:

    ((dynamic))item).ParentCollection = this;
    

    【讨论】:

    • 仅供参考,“为什么”非常简单:如果您尝试将IList&lt;TDerived&gt; 传递给接受IList&lt;TBase&gt; 的东西,那么您可以将.Add(Base item) 调用到派生列表中,不会的
    • 当然,我试过了,但是在派生类中实现接口导致必须声明 D.parent 但这会导致更多问题。我想从派生自 IList 的 observable 派生,所以......我想我将依赖类型检查而不是泛型
    【解决方案3】:

    为了感谢大家,我将发布我最终完成的设计

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ClassLibrary1
    {
        public class SortableCollection<T> : System.Collections.ObjectModel.ObservableCollection<T>, ISortableCollection where T : ISortable, IComparable, IComparable<T>
        {
            public new void Add(T item)
            {
                if (this.Items.Contains(item))
                    throw new InvalidOperationException("This list can contain the same item only once");
                base.Add(item);
            }
    
            public void Sort()
            {
                var sorted = this.Items.ToList();
                sorted.Sort();
                for (var i = 0; i < this.Items.Count; i++)
                {
                    if (object.ReferenceEquals(this.Items[i], sorted[i]))
                    {
                        this.Items[i].Index = i;
                        continue;
                    }
                    // if u want to support duplicates create a nextIndexOf and start searching from i
                    var previousIndex = IndexOf(sorted[i]);
                    Move(previousIndex, i);
                }
            }
    
            protected override void InsertItem(int index, T item)
            {
                item.Index = index;
                item.ParentCollection = this;
                base.InsertItem(index, item);
            }
    
            protected override void RemoveItem(int index)
            {
                this.Items[index].ParentCollection = null;
                base.RemoveItem(index);
            }
    
            protected override void ClearItems()
            {
                foreach (var item in this.Items)
                    item.ParentCollection = null;
                base.ClearItems();
            }
    
            protected override void SetItem(int index, T item)
            {
                this.Items[index].ParentCollection = null;
                item.Index = index;
                item.ParentCollection = this;
                base.SetItem(index, item);
            }
    
            protected override void MoveItem(int oldIndex, int newIndex)
            {
                this.Items[oldIndex].Index = newIndex;
                this.Items[newIndex].Index = oldIndex;
                base.MoveItem(oldIndex, newIndex);
            }
        }
    
        public interface ISortableCollection : IList
        {
            void Sort();
        }
    
        public interface ISortable
        {
            Int32 Index { get; set; }
            ISortableCollection ParentCollection { get; set; }
        }
    
        public class BaseClass : ISortable, IComparable, IComparable<BaseClass>
        {
            public int Index { get; set; }
    
            public ISortableCollection ParentCollection { get; set; }
    
            public int CompareTo(object obj)
            {
                return CompareTo(obj as BaseClass);
            }
    
            public int CompareTo(BaseClass other)
            {
                if (other == null)
                    return 1;
                return this.Index.CompareTo(other.Index);
            }
        }
    
        public class DerivedClass : BaseClass { }
    
        public class Controller
        {
            SortableCollection<BaseClass> MyBaseSortableList = new SortableCollection<BaseClass>();
            SortableCollection<DerivedClass> MyDerivedSortableList = new SortableCollection<DerivedClass>();
    
            public Controller()
            {
                //do things
                MyDerivedSortableList.Add(new DerivedClass());
                MyDerivedSortableList.Add(new DerivedClass());
                var derivedThing = new DerivedClass();
                MyDerivedSortableList.Add(derivedThing);
                var sibiling = derivedThing.ParentCollection[derivedThing.Index - 1] as BaseClass;  //way easier
                // switch the two objects order and call sort
                // calling a sort before the operation if indexes have been messed with
                // add an event to ISortable to notify the list the index has been changed and mark the list dirty
                derivedThing.Index -= 1;
                sibiling.Index += 1;
                derivedThing.ParentCollection.Sort();   // maybe the list was created where i couldn't access it
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-09-29
      • 2013-01-11
      • 2020-11-20
      • 2011-02-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多