tags:

views:

362

answers:

4

I'm new to C# and don't understand why the following code doesn't work.

public static Nullable<T> CoalesceMax<T>(Nullable<T> a, Nullable<T> b) where T : IComparable
{
    if (a.HasValue && b.HasValue)
        return a.Value.CompareTo(b.Value) < 0 ? b : a;
    else if (a.HasValue)
        return a;
    else
        return b;
}

// Sample usage:
public DateTime? CalculateDate(DataRow row)
{
    DateTime? result = null;
    if (!(row["EXPIRATION_DATE"] is DBNull))
        result = DateTime.Parse((string)row["EXPIRATION_DATE"]);
    if (!(row["SHIPPING_DATE"] is DBNull))
        result = CoalesceMax(
            result
            DateTime.Parse((string)row["SHIPPING_DATE"]).AddYears(1));
    // etc.
    return result;
}

It gives the following error during compilation:

The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable<T>'
+14  A: 

You need to add a T : struct constraint:

public static Nullable<T> CoalesceMax<T>
    (Nullable<T> a, Nullable<T> b) where T : struct, IComparable

Otherwise C# will try to work out what Nullable<T> means, and realise that it doesn't already have the constraint required by Nullable<T> itself. In other words, you could try to call:

CoalesceMax<string>(...)

which wouldn't make sense, as Nullable<string> isn't valid.

Jon Skeet
A: 

Your generic method is using a Nullable<T>.

However, you aren't constraining the type of T, so it could end up being Nullable<Form>, which is obviously invalid.

You need to change the constraint to where T : struct, IComparable to ensure that T can only be a value type.

SLaks
+3  A: 

The Nullable<T> type has a constraint on it that requires T to be a value type (struct in C#). That's why the compiler is telling you about Nullable<T> and not your function or the call site of that function -- it's the Nullable class that is the root cause of the error, so this is actually more helpful that if the compiler just pointed to your function and said "this ain't right, fix it!" (Imagine if CoalesceMax used several generics, and violated the constraint on only one of them - it's more useful to know which generic had its constraint broken than to just know that one or more constraints in CoalesceMax were broken).

The solution is to make your T and their T compatible by introducing the same constraint. This is done by adding the struct constraint, which must come before all interface / new constraints:

public static Nullable<T> CoalesceMax<T>(Nullable<T> a, Nullable<T> b) where T : struct, IComparable{
  ...
}
Josh Petrie
A: 

Hi guys, Do you think even the functon call CalculateDate(DataRow row) above would compile without errors? I was trying to compile this and came across the following error.

The type arguments for method 'CoalesceMax(T?, T?)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

In my mind, this happens because the args passed to the function are of the type DateTime? and DateTime as opposed to DateTIme? and DateTime? which I believe is what is expected by the compiler.

I can think of two ways of fixing this problem.

a) result = CoalesceMax<DateTime>(result, DateTime.Parse((string)row["SHIPPING_DATE"]).AddYears(1));

b) result = CoalesceMax(result, (DateTime?) DateTime.Parse((string)row["SHIPPING_DATE"]).AddYears(1));

Question: Does this mean that the compiler is not making use of the implicit conversion defined from DateTime to DateTime?

Am I on the right track or missing anything?

Appreciate if anyone could shed some light here!

Thanks.

RanC