views:

282

answers:

3

I have a collection class with an Equals method that I want to pass in a method to do equality checking between each item. Furthermore, I want to allow the delegate type to operate on superclasses of T as well as T itself:

public delegate bool EqualityComparer<T>(T x, T y);

public class Collection<T>
{
    //...

    public bool Equals<U>(Collection<T> other, EqualityComparer<U> eq) where T : U
    {
        // code using eq delegate to test equality between
        // members of this and other collection
    }
}

Unfortunately, the compiler borks over this ('Collection.Equals()' does not define type parameter 'T'). Is there any way of specifying this type of constraint/operation?

+1  A: 

No, I'm afraid you can't specify a constraint like that. (I've wanted it too on occasion.)

You could write a static generic method with two type parameters in a non-generic class though:

public delegate bool EqualityComparer<T>(T x, T y);

public class Collection
{
    public static Equals<T, U>(Collection<T> first,
                               Collection<T> second,
                               EqualityComparer<U> comparer) where T : U
    {

    }
}

and you could even make that call an instance method on the generic class if you like:

// Implementing the static method:
return first.Equals(second, new EqualityComparer<T>(comparer));

where the collection's instance method would just be:

public bool Equals(Collection<T> other, EqualityComparer<T> eq)
{
    // ...
}

This uses the contravariance available for creating delegates from C# 2 onwards.

Jon Skeet
Does it make sense to be possible? Wouldn't making it possible by inference be hard because the two declarations can be spread in the whole project, or even not be specified
Dykam
Yes, it makes perfect sense - you can specify that a type parameter has to be a *subclass* of something else, so why not the other way round? (Java allows this, using `T super Foo` instead of `T extends Foo`, btw.)
Jon Skeet
Ah - I see you were editing your answer to put what I was writing as a rider :)
ShuggyCoUk
Yup - was trying something else first, but then this seemed the best way forward.
Jon Skeet
A: 

If you explicitly want the type parameter to be an inherited class of U, you don't need generics, but rather use U as the formal type of the parameters. And there polimorfism kicks in! :)

Tamás Szelei
+1  A: 

As Jon said you cannot reference the T within the constrain in that way since it is declared at the class level.

If you can write the method without access to the private state of the collection (or with their being internal) then you can rewrite like so:

public class Collection<T>
{
    ...
}

public static class CollectionExtentions
{
    public static bool Equals<T,U>(
            this Collection<T> first, 
            Collection<T> other, 
            EqualityComparer<U> eq) where T : U
    {
            ... // legal to use eq here on the T values with collections
    }
}

Incidentally I suggest you use Func<T,T,bool> rather than your own named delegate

ShuggyCoUk
This is in .NET 2, so Func isn't available :(
thecoop
Ah - downer :( you might want to add that rider to the question (since obviously you wouldn't be able to use the extension method's syntax so Jon's suggestion of the static method being used by the instance method is best)
ShuggyCoUk