In Java 5 you can use the following syntax for variance in generic types:
public class A {...}
public class AA extends A {...}
public class MyContainer<E>
{
void setComparer(Comparator< super E> c) {...}
...
}
This means that you can pass instances of either Comparer<A> or Comparer<AA> to MyContainer<AA>.setComparer.
Is there a way to specify this type of constraint in .NET The where clause can be used to restrict the type the OTHER way ("where type inherits from another type") but can I define "where type is base class of another type" In other words I need something like the following:
public class MyContainer<T>
{
void SetComparer<U>(IComparable<U> c) where U : super T {...}
...
}
Thank you for any ideas (but flames about my stupidity are not welcome ;););) ).
Milan

How to constrain generic type to a superclass instead a subclass of another type?
Jazz1993
Given your sample of 2 classes A and AA (where AA derives from A) I assume that you implement IComparable and IComparable<> in each class or even in a separate class. Here is my sample code equating this:
class A { ... }
class AA : A { ... }
class ComparatorA : IComparable, IComparable<A> { ... }
class ComparatorAA : ComparatorA, IComparable<AA> { ... }
Notice that I used a separate class for the comparators here as is typical but A and AA could have just as easily implemented the interfaces themselves. The important thing however is that ComparatorAA derives from ComparatorA. Therefore ComparatorAA actually implements 3 interface: IComparable, IComparable<A> and IComparable<AA>. That is the key for working with derived classes. Since A and AA both use a comparator that derives from the same class you can now define your generic class as follows:
class Container<T>
{
void SetComparator ( IComparable<T> comparator ) { ... }
}
Note that you could also use IComparable here and it would still work correctly. However by explicitly specifying the type you are limiting it to only those that support comparison against the container contents. The following code can then be used:
Container<A> myContainer = new Container<A>();
myContainer.SetComparator(new ComparatorA()); or
myContainer.SetComparator(new ComparatorAA());
Note however that this wouldn't work:
Container<AA> myContainer = new Container<AA>();
myContainer.SetComparator(new ComparatorA());
And this shouldn't work because you are assuming that the base class is sufficiently informed enough to be able to compare objects that might differ in the derived class fields. That is why virtual functions work the way they do. Only the derived class is knowledgeable enough to determine if two of its instances are equivalent (or whatever).
Hope this helps,
Michael Taylor - 10/8/05
ubm0158
I'm not convinced this additional functionality is really that useful. I posted a full treatment of my thoughts on my blog here: http://www.srtsolutions.com/public/item/109651
Short excerpts: "The book example from the above link is interesting, but I can easily provide two possible modifications within the C# language that work just as well, or even better."
And, my opinion of the Book example posted above:
"I’m not sure the original NamedObjectComparer has real meaning (or at least that it’s really what the author intended). The NamedObjectComparer can be used to compare any two objects that have names. Do you really want to compare a Book to an Employee Or a Street to a Customer That’s valid with this object, but I doubt that have a proper semantic meaning.
Of course, if you really did intend to compare different types that both have names, simply change the declaration of the collection:
new SortedCollection<INamedObject>(new NamedObjectComparer());"
Comments weclome.
Francois Malgreve
Note that in a previous life I was the guy who came up with wildcards for Java, and ran the group who implemented them. So it is not that I don't think about it! :-)
Mads
KamalHWZ
Suppose the following interface
interface INamedObject { string Name {get;} }
and the following IComparer class
class NamedObjectComparer : IComparer
{
public int Compare(T a, T b)
{
return String.Compare(a.Name, b.Name);
}
}
This comparer class is good for comparing instances of any class that implement INamedObject, so if we have a sorted collection class as follows,
class SortedCollection
{
public SortedCollection(IComparer
}
then of course we can create a collection of INamedObjects that are enumerated in alphabetical order, thus
new SortedCollection
However, consider a concrete implementation of INamedObject,
class Book : INamedObject
{
private string title;
...
public string Name {get {return title;} }
}
The NamedObjectComparer class could reasonably be used to sort Books alphabetically. It doesn't require any specific knowledge of the Book class other than its name. Unfortunately, we can't create a sorted collection of Books using SortedCollection's constructor
new SortedCollection
because NamedObjectComparer implements IComparer<INamedObject>, not IComparer<Book>. The point is that if you define a generic comparer class, you shouldn't have to sub-class it for each actual type you want to compare. That's supposed to be one of the key advantages of generic code.
Anyway, I guess the upshot is that there doesn't seem to be an easy way to define the constructor of SortedCollection<T> such that is will accept a comparer of anything other than T. You could define multiple type parameters on the class declaration itself
class SortedCollection
but this solution doesn't scale well - for each method of SortedCollection where this problem occurs, an additional type parameter would have to be introduced.