views:

542

answers:

4

I don't understand what is going on here...

I've got the following error: The type 'TestApp.TestVal' cannot be used as type parameter 'T' in the generic type or method 'TestApp.SomeClass<T>'. There is no boxing conversion from 'TestApp.TestVal' to 'System.IComparable<TestApp.TestVal>'.

This error happens for the following code:

public enum TestVal
{
    First,
    Second,
    Third
}

public class SomeClass<T>
    where T : IComparable<T>
{
    public T Stored
    {
        get
        {
            return storedval;
        }
        set
        {
            storedval = value;
        }
    }
    private T storedval;
}

class Program
{
    static void Main(string[] args)
    {
        //Error is on the next line
        SomeClass<TestVal> t = new SomeClass<TestVal>(); 
    }
}

Since the enum is an int by default and int's implement the IComparable<int> interface it seems like there shouldn't be an error....

A: 

In C# enums implement IComparable, but not the generic IComparable<T>. I'm not sure why this is, but maybe you could switch to the non-generic IComparable in your where clause.

Andy White
+3  A: 

Enums do not derive from System.Int32s - they derive from System.Enum, which doesn't implement IComparable<int> (it does implement IComparable, though).

Although an enum's underlying value is an int by default, the enum itself isn't. Thus, there is no conversion between the two.

Mark Seemann
(I fixed the markdown - and then went crazy and edited your answer instead of my own! Fixed now; sorry about that...)
Marc Gravell
Thanks for the explanation. That helps. It also makes me wonder if I will have the same kind of problem with structs....
+3  A: 

Firstly, I'm not sure whether it is sensible to use IComparable<T> with an enum... IEquatable<T>, sure - but comparison?

As a safer alternative; rather than mandate the IComparable<T> with the generic constraint, perhaps use Comparer<T>.Default inside the class. This has the advantage of supporting IComparable<T> and IComparable - and it means you have less constraints to propagate.

For example:

public class SomeClass<T> { // note no constraint
    public int ExampleCompareTo(T other) {
        return Comparer<T>.Default.Compare(Stored, other);
    }
    ... [snip]
}

This works fine with the enum:

SomeClass<TestVal> t = new SomeClass<TestVal>();
t.Stored = TestVal.First;
int i = t.ExampleCompareTo(TestVal.Second); // -1
Marc Gravell
Thanks for the possible alternate solution to my problem. The actual code is very deep in a test framework that needs to handle just about any type of test data. I'll have to do some more investigation but it seems like your code might be a better way to deal with the compare in my generic objects....
A: 

Enum doesn't implement IComparable<T>, but it does implement IComparable. So an enum can be the T in a where clause like:

    where T : IComparable

but this gives an error:

    where T : IComparable<T>

And then I suppose you'd like SomeClass to be comparable. To do that, it would have to implement IComparable itself.

Here's an example of both (using a public member to keep the code simple):

public class SomeClass<T>
    : IComparable<SomeClass<T>>
    where T : IComparable
{
    public T storedval;

    public int CompareTo(SomeClass<T> other)
    {
        return storedval.CompareTo(other.storedval);
    }
}
Andomar