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)
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 .
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 .
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.
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.
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.
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]);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With