Search code examples
c#genericsnullable

Returning nullable and null in single C# generic method?


Is it possible in a C# generic method to return either an object type or a Nullable type?

For instance, if I have a safe index accessor for a List and I want to return a value that I can check later with either == null or .HasValue().

I currently have the following two methods:

static T? SafeGet<T>(List<T> list, int index) where T : struct 
{
    if (list == null || index < 0 || index >= list.Count)
    {
        return null;
    }

    return list[index];
}

static T SafeGetObj<T>(List<T> list, int index) where T : class
{
    if (list == null || index < 0 || index >= list.Count)
    {
        return null;
    }

    return list[index];
}

If I try to combine the methods into a single method.

static T SafeGetTest<T>(List<T> list, int index)
{
    if (list == null || index < 0 || index >= list.Count)
    {
        return null;
    }

    return list[index];
}

I get a compile error:

Cannot convert null to type parameter 'T' because it could be a non-nullable value type. Consider using 'default(T)' instead.

But I don't want to use default(T) because in the case of primitives, 0, which is the default for an int, is a possible real value that I need to distinguish from a not available value.

Is it possible for these methods be combined into a single method?

(For the record I am using .NET 3.0 and while I interested in what more modern C# can do I will personally only be able to use answers that work in 3.0)


Solution

  • Not precisely what you want, but a possible workaround would be to return a Tuple (or other wrapper class):

        static Tuple<T> SafeGetObj<T>(List<T> list, int index) 
        {
            if (list == null  || index < 0 || index >= list.Count)
            {
                return null;
            }
    
            return Tuple.Create(list[index]);
        }
    

    Null would always mean that no value could be obtained, the single tuple itself would mean a value (even if the value itself can be null).

    In vs2015 you could use the ?. notation when calling: var val = SafeGetObj(somedoublelist, 0)?.Item1; Of course instead of a Tuple, you could create your own generic wrapper.

    As stated, not exactly optimal, but it would be a workable work around, and have the added benefit of being able to see the difference between not a valid selection and a null element.


    Example of a custom wrapper implementation:

        struct IndexValue<T>
        {
            T value;
            public bool Succes;
            public T Value
            {
                get
                {
                    if (Succes) return value;
                    throw new Exception("Value could not be obtained");
                }
            }
    
            public IndexValue(T Value)
            {
                Succes = true;
                value = Value;
            }
    
            public static implicit operator T(IndexValue<T> v) { return v.Value; }
        }
    
        static IndexValue<T> SafeGetObj<T>(List<T> list, int index) 
        {
            if (list == null || index < 0 || index >= list.Count)
            {
                return new IndexValue<T>();
            }
    
            return new IndexValue<T>(list[index]);
        }