Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)

like image 844
James McMahon Avatar asked Aug 31 '15 15:08

James McMahon


People also ask

Can I return null from a function in C?

You don't. NULL is a macro representing a value of type void * . It is not an int , so a function returning int cannot return NULL . Now, it is possible to convert NULL or any other pointer to type int , but the result of such a conversion is a valid, ordinary int .

Can we return null in int return type?

int is a primitive data type . It is not a reference variable which can take null values . You need to change the method return type to Integer wrapper class .

Is it better to return null or empty?

Returning null is usually the best idea if you intend to indicate that no data is available. An empty object implies data has been returned, whereas returning null clearly indicates that nothing has been returned.

Is it okay to return null?

Returning Null is Bad Practice The FirstOrDefault method silently returns null if no order is found in the database. There are a couple of problems here: Callers of GetOrder method must implement null reference checking to avoid getting a NullReferenceException when accessing Order class members.


Video Answer


2 Answers

There is one more option here you may not have considered...

public static bool TrySafeGet<T>(IList<T> list, int index, out T value)
{
    value = default(T);

    if (list == null || index < 0 || index >= list.Count)
    {    
        return false;
    }

    value = list[index];
    return true;
}

Which lets you do things like this:

int value = 0;
if (!TrySafeGet(myIntList, 0, out value))
{
    //Error handling here
}
else
{
    //value is a valid value here
}

And on the top side, its compatible with the TryXXX of many other type collections, and even the conversion/parsing API's. Its also very apparent of what the function is from the name of the method, it "tries" to get the value, and if it can't, it returns false.

like image 172
Ron Beyer Avatar answered Oct 20 '22 16:10

Ron Beyer


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]);
    }
like image 23
Me.Name Avatar answered Oct 20 '22 16:10

Me.Name