2015-04-04 2 views
4

Я хотел создать наблюдаемый набор, сортируемый , поэтому я начал создавать класс, который наследует наблюдаемые с помощью некоторых методов для его сортировки, тогда я хотел, чтобы этот класс сохранял индекс в дочерних элементах, поэтому я создал интерфейс, который выставляет свойство index, в котором я могу писать, и я оценил 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<DerivedClass>, но типы несоответствия ... это правильный способ сделать это?

Точной ошибка

Ошибка 1 Тип «ClassLibrary1.DerivedClass» не может быть использована в качестве параметра типа «Т» в универсальном типе или методе 'ClassLibrary1.SortableCollection<T>'. Нет никакого неявного преобразования ссылок из класса ClassLibrary1.DerivedClass в 'ClassLibrary1.ISortable<ClassLibrary1.DerivedClass>'. C: \ Users \ luigi.trabacchin \ документы \ Visual Studio 2013 \ Projects \ ClassLibrary1 \ ClassLibrary1 \ Class1.cs 48 89 ClassLibrary1

ответ

7

Проблема заключается в том, что ваше ограничение на T является «T обязан быть I<T>», и вы прошли DerivedClass для T, но DerivedClass не конвертируется в I<DerivedClass>, он может быть конвертирован в I<BaseClass>.

Я не знаю, что вы пытаетесь представить с ограничением, которое T будет I<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 он должен использовать только T в выходе позиций. List<T> использует T в обоих положениях ввода и вывода, поэтому он не может быть ковариантным или контравариантным.

+0

Я немного поиграл с этим фрагментом кода, и я не смог решить проблему с изменением и базовый класс IList удален, потому что ParentCollection находится как внутри, так и вне. – usr

+0

Эй, спасибо Эрику за действительно хороший ответ. То, что я хотел достичь, было также, чтобы заставить братьев и сестер текущего элемента переключить их порядок, а затем вызвать сортировку ... Я думаю, что я просто проверю тип родительской коллекции и вызову метод, если родительская коллекция реализует интерфейс ... Я сблизился с ограничениями, но это действительно неудобно и все еще что-то не хватает. –

1

Вы должны DerivedClass быть ISortable<DerivedClass>:

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

Co- и контравариантность на T не может работать здесь, потому что вы получаете от IList<T>, который является инвариантным.

Даже удаляя IList<T> и удаляя геттер, я не могу заставить его работать прямо сейчас с изменением. Не совсем моя сила. Это часть системы типов, которая лучше оставлена ​​одна, если вы можете ей помочь.

Если система типа делает вашу голову взорваться рассмотреть динамическое решение:

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

FYI «почему» довольно прост: если вы пытаетесь передать в '' IList , к что-то, что принимает в 'IList ', что позволит вам вызывать '.Add (базовый элемент)' в производный список, который будет no-no –

+0

Конечно, я попытался, но реализация интерфейса в производном классе приводит к необходимости объявлять D.parent , но это приводит к дальнейшим проблемам. Я хотел получить от наблюдаемого, которые происходят от IList, поэтому ... Я полагаюсь на проверку типов вместо генериков –

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 
     } 
    } 
} 
Смежные вопросы