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)
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]);
}