通用接口中的协方差

时间:2015-04-04 15:00:12

标签: c# .net generics variance

我想创建一个可排序的observableCollection 所以我开始创建一个继承observable的类,用一些方法对它进行排序,然后我希望该类将索引保存到子节点中,所以我创建了一个接口,它暴露了一个索引属性,我可以写入,并且我花费了T我的集合类是我的接口,然后我希望能够从avery项访问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<DerivedClass>,但类型不匹配......这是正确的做法吗?

确切的错误是

  

错误1类型&#39; ClassLibrary1.DerivedClass&#39;不能用作类型参数&#39; T&#39;在通用类型或方法'ClassLibrary1.SortableCollection<T>'中。没有来自ClassLibrary1.DerivedClass&#39;的隐式引用转换。到'ClassLibrary1.ISortable<ClassLibrary1.DerivedClass>'。 c:\ users \ luigi.trabacchin \ documents \ visual studio 2013 \ Projects \ ClassLibrary1 \ ClassLibrary1 \ Class1.cs 48 89 ClassLibrary1

3 个答案:

答案 0 :(得分:8)

问题是您对T的约束是“T必须是I<T>”,而您已经DerivedClass传递了T ,但DerivedClass无法转换为I<DerivedClass>,可转换为I<BaseClass>

我不知道您要使用TI<T>的约束来表示什么。我知道人们经常使用这种模式来试图表示C#类型系统实际上没有实现的约束。有关详细信息,请参阅我关于该主题的文章:

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

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

I<D>无法转换为I<B>的原因是,为了使方差有效,必须将接口标记为支持方差;使用Tout标记in,具体取决于您是否需要协方差或逆变。

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

为了使接口在T中具有协变性,它只需要在输出位置使用TList<T>在输入和输出位置使用T,因此它不能是协变或逆变。

答案 1 :(得分:2)

您需要DerivedClass成为ISortable<DerivedClass>

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

T上的共同和逆转在这里不起作用,因为你是从不变的IList<T>派生出来的。

即使删除IList<T>并移除吸气剂,我也无法立即使用差异。不完全是我的力量。这是类型系统的一部分,如果你能提供帮助,最好不要单独使用它。

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

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

答案 2 :(得分:0)

为了感谢你,我将发布我最终的设计

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