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