I have a generic method and need to guarantee that my method returns nullable<T>
(ex. source is List<int>
or List<int?>
must returns int?
) and I want to return null instead of default (ex. default of int is 0. in this case I want to return null). It can be possible to add constraints on type parameter(https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters) but whit this approach I must have 2 methods (one of them with struct constraint and another with class constraints)
my method is:
public static T? ElementOrNull<T>(List<T> source, int? index)
{
if (source == null || index == null || source.Count <= index)
return null;
return source[index.Value];
}
I try Convert.ChangeType()
and reflection(Activator.CreateInstance()
and Activator.CreateInstance<T?>()
) but I can't solve this problem.
Any and all feedback is appreciated. Thank you
Note: I checked all the questions about generics and casting of them in StackOverflow but they didn't help me
Generics constraints are not part of the method signature, but compiler still needs to know if T
is value or reference type to convert null
to T
and handle ?
in T?
correctly. But you can help the compiler using optional constrained parameters this way:
public class ReqStruct<T> where T : struct
{
}
public class ReqClass<T> where T : class
{
}
public static T? ElementOrNull<T>(List<T> source, int? index, ReqStruct<T> req = null) where T : struct
{
if (source == null || index == null || source.Count <= index)
return null;
return source[index.Value];
}
public static T? ElementOrNull<T>(List<T> source, int? index, ReqClass<T> req = null) where T : class
{
if (source == null || index == null || source.Count <= index)
return null;
return source[index.Value];
}
And since the parameters are optional the usage will look like:
var intOrNull = MethodHolder.ElementOrNull(new List<int> { 1 }, 5); // var is int?
var objOrNull = MethodHolder.ElementOrNull(new List<object>(), 5); // var is object?
My preferred approach to this is to use a custom Option/Maybe type. I.e. a struct with a generic value and a bool to describe if the value is valid or not. I.e. never use null, always use a Maybe
for optional parameters/return values:
public static Maybe<T> ElementOrNone<T>(List<T> source, Maybe<int> index)
{
if (source == null || !index.HasValue || source.Count <= index.Value)
return Maybe<T>.None;
return source[index.Value];
}
While Nullable<T>
and non-nullable references help to avoid null reference exceptions, I find them lacking when writing generic code, in part due to the problem you are describing.
Using a custom type have the advantage of consistent behavior for both value and reference types, and can allow for various extensions to make working with values easier, potentially even using linq query syntax to combine multiple values. A downside is that it is a custom type, so more to learn.
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