Covariance in generic interfaces

后端 未结 3 1285
傲寒
傲寒 2021-01-13 05:17

I wanted to create an observableCollection that is sortable so i started creating a class that inherit observable with some methods to sort it, then i wanted that class to p

相关标签:
3条回答
  • 2021-01-13 05:57

    You need DerivedClass to be a ISortable<DerivedClass>:

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

    Co- and contravariance on T cannot work here because you are deriving from IList<T> which is invariant.

    Even removing IList<T> and removing the getter I can't get it to work right now with variance. Not exactly a strength of mine. This is a part of the type system that is better left alone if you can help it.

    If the type system makes your head explode consider a dynamic solution:

    ((dynamic))item).ParentCollection = this;
    
    0 讨论(0)
  • 2021-01-13 06:17

    The problem is that your constraint on T is "T is required to be an I<T>", and you have passed a DerivedClass for T, but DerivedClass is not convertible to I<DerivedClass>, it is convertible to I<BaseClass>.

    I don't know what you are trying to represent with the constraint that T be an I<T>. I do know that people often use this pattern to try to represent a constraint that the C# type system does not actually implement. See my article on the subject for details:

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

    I encourage you to simplify things considerably; you seem to be trying to capture too much in the type system.

    The reason why I<D> is not convertible to I<B> is because in order for variance to work, the interface must be marked as supporting variance; mark the T with out or in depending on whether you want covariance or contravariance.

    However, since IList<T> is invariant, it will not be legal to make the derived interface covariant or contravariant. Consider IEnumerable<T> instead, as it is covariant in T.

    In order for an interface to be covariant in T it needs to only use T in output positions. List<T> uses T in both input and output positions, so it cannot be covariant or contravariant.

    0 讨论(0)
  • 2021-01-13 06:21

    To thank you all i'm going to post the design i've end up with

    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
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题