4

I'm creating a generic method which should be able to return null.

As an example I've created this method GetSecondSmallest(List<T>) below. This function loops through all of the IComparable items and returns the second smallest item if possible. If this item does not exist it returns null.

public T? GetSecondSmallest<T>(List<T> items) where T : Nullable, IComparable
{
    if (items.Count == 0) return null;

    T smallest = items[0];
    T? secondSmallest = null;

    // Find second smallest by looping through list

    return secondSmallest;
}

Two example usages of this method would be:

GetSecondSmallest(new List<int> {4, 2, 3, 1, 6, 8}) // Should return 2
GetSecondSmallest(new List<MyComparable> {new MyComparable('x')}) // Should return null

When you try to compile this code you'll see what the problem is here:

Error CS0717 'Nullable': static classes cannot be used as constraints

How can I make this method return either an instance of T or null?


EDIT: I have already seen this question, but this does not answer my question, because in my situation the parameter is also generic, and should be Nullable. So the provided answers there aren't applicable.

Community
  • 1
  • 1
Duncan Lukkenaer
  • 12,050
  • 13
  • 64
  • 97
  • 1
    @j0ey_wh The `INullable` interface is designed for use with databases. It's not an interface that `Nullable` implements. – RB. Oct 04 '16 at 13:30

3 Answers3

4

You can't narrow T down as nullable or an object. You have to make two methods, one for T and one for T?.

public T? GetSecondSmallestAsNullable<T>(List<T> items) where T : struct
{
    return null;
}

public T GetSecondSmallestAsClass<T>(List<T> items) where T : class
{
    return null;
}
Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
  • So basically duplicate code is the best solution here? There must be a better way – Duncan Lukkenaer Oct 04 '16 at 13:59
  • 2
    Rewrite the .NET framework to better support nullable with generics? – Patrick Hofman Oct 04 '16 at 14:00
  • 3
    Another approach could be to look at using an [Option type](https://www.google.co.za/webhp?#q=option+type+in+C%23) in C# (as a wrapper generic) to indicate to the caller that there is no match on the predicate. This will then remove the struct v class dilemma with nullable types. It does however require significant refactoring of code / introduction of FP framework code. – StuartLC Oct 04 '16 at 14:11
1

Change the return type to Nullable, and call the method with the non nullable parameter

Simple example:

static void Main(string[] args)
{
    int? i = GetValueOrNull<int>(null, string.Empty);
}


public static Nullable<T> GetValueOrNull<T>(DbDataRecord reader, string columnName) where T : struct
{
    object columnValue = reader[columnName];

    if (!(columnValue is DBNull))
        return (T)columnValue;

    return null;
}

Nullable type as a generic parameter possible?

UPD

Like this:

public Nullable<T> GetSecondSmallest<T>(List<T> items) where T : IComparable
{
    if (items.Count == 0) return null;

    Nullable<T> smallest = items[0];
    Nullable<T> secondSmallest = null;

    // Find second smallest by looping through list

    return secondSmallest;
}
Community
  • 1
  • 1
Taras Kovalenko
  • 2,323
  • 3
  • 24
  • 49
1

Use a wrapper class such as Tuple to wrap the resulting value. In the example below, null is returned if there is no second smallest item, otherwise a Tuple is returned with the second smallest value.

public static void Main()
{
    var result1 = GetSecondSmallest(new List<int>
    {
    4, 2, 3, 1, 6, 8
    }); // Should return 2

    var result2 = GetSecondSmallest(new List<MyComparable>
    {
    new MyComparable('x')
    }); // Should return null

    Console.WriteLine("result1=" + result1.Item1);
    Console.WriteLine("result2 == null: " + (result2 == null));
}

public static Tuple<T> GetSecondSmallest<T>(List<T> items)where T : IComparable
{
    return items.Skip(1).Select(v => Tuple.Create(v)).FirstOrDefault();
}
Grax32
  • 3,986
  • 1
  • 17
  • 32