Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cast T To Nullable<T>

Tags:

c#

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

like image 972
Mohsen Koorani Avatar asked Nov 15 '21 08:11

Mohsen Koorani


2 Answers

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?
like image 87
Guru Stron Avatar answered Oct 20 '22 11:10

Guru Stron


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.

like image 26
JonasH Avatar answered Oct 20 '22 12:10

JonasH