16

Why am I getting this error in the following code?

void Main()
{
    int? a = 1;
    int? b = AddOne(1);
    a.Dump();
}

static Nullable<int> AddOne(Nullable<int> nullable)
{
    return ApplyFunction<int, int>(nullable, (int x) => x + 1);
}

static Nullable<T> ApplyFunction<T, TResult>(Nullable<T> nullable, Func<T, TResult> function)
{
    if (nullable.HasValue)
    {
        T unwrapped = nullable.Value;
        TResult result = function(unwrapped);
        return new Nullable<TResult>(result);
    }
    else
    {
        return new Nullable<T>();
    }
}
Artemix
  • 2,113
  • 2
  • 23
  • 34
Clive
  • 1,128
  • 2
  • 11
  • 19

2 Answers2

20

There are several problems with the code. The first one is that your types must be nullable. You can express that by specifying where T: struct. You will also need to specify where TResult: struct because you're using that as a nullable type too.

Once you fix up the where T: struct where TResult: struct you also need to change the return value type (which was wrong) and a number of other things too.

After fixing all those errors and simplifying, you wind up with this:

static TResult? ApplyFunction<T, TResult>(T? nullable, Func<T, TResult> function)
                where T: struct 
                where TResult: struct
{
    if (nullable.HasValue)
        return function(nullable.Value);
    else
        return null;
}

Note that you can rewrite Nullable<T> as T? which makes things more readable.

Also you could write that as one statement using ?: but I don't think it's as readable:

return nullable.HasValue ? (TResult?) function(nullable.Value) : null;

You might want to put this into an extension method:

public static class NullableExt
{
    public static TResult? ApplyFunction<T, TResult>(this T? nullable, Func<T, TResult> function)
        where T: struct
        where TResult: struct
    {
        if (nullable.HasValue)
            return function(nullable.Value);
        else
            return null;
    }
}

Then you can write code like this:

int? x = 10;
double? x1 = x.ApplyFunction(i => Math.Sqrt(i));
Console.WriteLine(x1);

int? y = null;
double? y1 = y.ApplyFunction(i => Math.Sqrt(i));
Console.WriteLine(y1);
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • 2
    Instead of writing `new TResult?()`, isn't it more clear to just write `null`? That's the philosophy of `Nullable<>`, I guess. – Jeppe Stig Nielsen May 24 '13 at 08:56
  • Instead of: return nullable.HasValue ? (TResult?) function(nullable.Value) : null; you could do return nullable??null; which is a bit nicer and more readable – Steve Temple May 24 '13 at 10:04
  • @SteveTemple How then does `function()` get called? – Matthew Watson May 24 '13 at 10:19
  • Sorry misread the line, you're right if you want to call a function that won't work – Steve Temple May 24 '13 at 10:29
  • you state that `return nullable.HasValue ? (TResult?) function(nullable.Value) : null;` isn't so readable (which i agree), but if you add a parameter to that extension method: `, TResult resultWhenNull = default(TResult)` then you can make the method body a lot more readable with `return (nullable.HasValue) ? function(nullable.Value) : resultWhenNull;`. Plus it adds a feature: you can specify the default value when your nullable is null, but it's only an optional parameter, so it's not an inconvenience that must always be passed. ;) – Shawn Kovac Mar 02 '15 at 17:29
  • @ShawnKovac Your proposed `resultWhenNull` is of type `TResult` which isn't a nullable type, which means you couldn't return a null from the method. Did you mean to make it type `TResult?` ? – Matthew Watson Mar 03 '15 at 09:36
  • @MatthewWatson thanks, i coded it the way i expressed in my method, but as you so correctly pointed out, that's not what i really wanted. i have now corrected my code so the new parameter is `, TResult? resultWhenNull = null`. Thanks!! – Shawn Kovac Mar 03 '15 at 21:47
  • so my full method is: `public static TResult? ApplyFunc(this T? nullable, Func function, TResult? resultWhenNull = null) where T : struct where TResult : struct { return (nullable.HasValue) ? function(nullable.Value) : resultWhenNull; }` – Shawn Kovac Mar 03 '15 at 21:49
  • so i think the body of that method now is more readable than if an `if` statement were used. ;) thanks for that correction, Matthew! i quite appreciate it! – Shawn Kovac Mar 03 '15 at 21:51
  • i just want to pipe in how nice C# is to us to forgive the omission of new lines where my code is all run together on one line, due to the comment restriction in this forum. i say 'phew!' that this is not a VB question! ;) – Shawn Kovac Mar 03 '15 at 21:55
  • @ShawnKovac Yep, that's a nice addition. :) – Matthew Watson Mar 04 '15 at 07:22
7

As the error suggests, the compiler has no guarantee that T won't already be nullable. You need to add a constraint to T:

static Nullable<T> ApplyFunction<T, TResult>(Nullable<T> nullable, 
    Func<T, TResult> function) : where T : struct 
                                 where TResult : struct
T. Kiley
  • 2,752
  • 21
  • 30