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 persist the index into the childs, so i created an interface that expose an index property where i can write to, and i costrainted the T of my collection class to be of my Interface, then i wanted to be able from avery item to access the parentCollection and here the problems started because the type of the parent collection is generic ... i've tried many solutions, and i think covariance or invariance is the way, but i can't get it working ...
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
}
}
}
this is more or less the setup.
I'd like to be able to create a SortableCollection<DerivedClass>
but the types mismatch... wich is the correct way of doing it ?
exact error is
Error 1 The type 'ClassLibrary1.DerivedClass' cannot be used as type parameter 'T' in the generic type or method
'ClassLibrary1.SortableCollection<T>'
. There is no implicit reference conversion from 'ClassLibrary1.DerivedClass' to'ClassLibrary1.ISortable<ClassLibrary1.DerivedClass>'
. c:\users\luigi.trabacchin\documents\visual studio 2013\Projects\ClassLibrary1\ClassLibrary1\Class1.cs 48 89 ClassLibrary1
Covariance and contravariance are terms that refer to the ability to use a more derived type (more specific) or a less derived type (less specific) than originally specified. Generic type parameters support covariance and contravariance to provide greater flexibility in assigning and using generic types.
In C#, covariance and contravariance enable implicit reference conversion for array types, delegate types, and generic type arguments. Covariance preserves assignment compatibility and contravariance reverses it.
A generic interface is primarily a normal interface like any other. It can be used to declare a variable but assigned the appropriate class. It can be returned from a method. It can be passed as argument. You pass a generic interface primarily the same way you would an interface.
Covariance means that a method can return a type that is derived from the delegate's return type. Contra-variance means that a method can take a parameter that is a base of the delegate's parameter type.
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.
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With