Nullable Type as a Generic Parameter Possible

Nullable type as a generic parameter possible?

Change the return type to Nullable<T>, and call the method with the non nullable parameter

static void Main(string[] args)
{
int? i = GetValueOrNull<int>(null, string.Empty);
}

public static Nullable<T> GetValueOrNull<T>(DbDataRecord reader, string columnName) where T : struct
{
object columnValue = reader[columnName];

if (!(columnValue is DBNull))
return (T)columnValue;

return null;
}

About Nullable of C# generic parameter

@canton7 explains why this does not work in a comment.

You cannot solve this with a constructor, as you will have to introduce a new type parameter and constructors cannot declare type parameters. Therefore I suggest using a factory method.

public class SomeClass<T>
{
public IList<T> List { get; private set; }

public static SomeClass<T> Create<U>(IList<Nullable<U>> list)
where U : struct, T
{
return new SomeClass<T> {
List = list
.OfType<T>()
.ToList()
};
}
}

Introducing the type parameter U allows us to add a type constraint that is limited to this method, while the type parameter T of the class remains unconstrained.

Note that a null value does not have a type. Therefore, OfType<T>() filters out nulls.

You can test it like this:

var ints = new List<int?> { 1, 2, null, 3 };
var sut = SomeClass<int>.Create(ints);
foreach (int item in sut.List) {
Console.WriteLine(item);
}

Prints:

1

2

3

Nullable generic type that can be an int or string

If your goal is just to have a generic type parameter that can be a reference type, value type, or nullable value type. Just remove the ?

public interface IAuditEntity<TKey>
{
TKey UpdatedBy { get; set; }
}

public class Bob : IAuditEntity<int?>
{
public int? UpdatedBy { get; set; }
}

If you are trying to use the nullable reference type feature, you will need to bump the version of your project

<LangVersion>9.0</LangVersion>
<nullable>enable</nullable>

Be warned : This is not fully supported, and likely to cause issues with certain language features, and not all attributes will be useable.

If your goal is to use reference types and value types, where the generic parameter is known to be a not nullable value type, and the reference types can be null, in C#8 or later you can use the notnull constraint without the ?

public interface IAuditEntity<TKey> where TKey : notnull
{
TKey UpdatedBy { get; set; }
}

If you would want to use reference types and value types, where the generic parameter is known to be not nullable, and the reference types can be null and also use the nullable reference type feature, you can use the notnull constraint and the nullable type operator ? C#9 or later

public interface IAuditEntity<TKey> where TKey : notnull
{
TKey? UpdatedBy { get; set; }
}

Nullable types in generic class

You are actually shooting yourself in the foot here. Arrays in .NET are automatically set to the default value of the type, so as long as a type's default is null (all objects and nullables), then simply creating the array is enough to set all items to null. Without that excess code, you may never have encountered an issue at all, depending on how you're trying to use this class.

If you're trying to allow cases like GenericClass<int> and have it expose an array of int?s, then do this. The where T : struct enforces that the supplied type is a value type, thereby allowing you to use T?. You can't use object types for the generic parameter in this case.

class GenericClass<T> where T : struct
{
public T?[] Arr { get; }
public GenericClass(int n)
{
Arr = new T?[n];
}
}

If you're trying to create a class that supports both object instances and nullables, try this. The problem here is you can't make a type constraint -- if you use where T : struct, you can't use object types; and if you use where T : class, you can't use nullables. Therefore, this type allows usages like GenericClass<MyObject> (works like you want), GenericClass<int?> (works like you want) and GenericClass<int> (doesn't work like you want, and can't possibly work like you want anyway). Unfortunately, there's no way to define this generic to disallow the latter case. You can make that check at runtime if you like, though.

class GenericClass<T>
{
public T[] Arr { get; }
public GenericClass(int n)
{
Arr = new T[n];
}
}

Nullable default parameter with generic type

All you need to do is have 2 overloads with different constraints

public static void Do<U>(U p = default) where U : class
{
Console.WriteLine(p is null ? "null" : p);
}

public static void Do<U>(U? p = default) where U : struct
{
Console.WriteLine(p is null ? "null" : p);
}

Live example: https://dotnetfiddle.net/iOzGDb

How to make Generic parameter T Nullable in C#?

Use a wrapper class such as Tuple to wrap the resulting value. In the example below, null is returned if there is no second smallest item, otherwise a Tuple is returned with the second smallest value.

public static void Main()
{
var result1 = GetSecondSmallest(new List<int>
{
4, 2, 3, 1, 6, 8
}); // Should return 2

var result2 = GetSecondSmallest(new List<MyComparable>
{
new MyComparable('x')
}); // Should return null

Console.WriteLine("result1=" + result1.Item1);
Console.WriteLine("result2 == null: " + (result2 == null));
}

public static Tuple<T> GetSecondSmallest<T>(List<T> items)where T : IComparable
{
return items.Skip(1).Select(v => Tuple.Create(v)).FirstOrDefault();
}

How to identify a nullable reference type for generic type?

In C# 8 with nullable enabled, is there a way to identify a nullable
reference type
for generic type?

In C# 8 there is NO way to check if a type parameter passed to a generic method is a nullable reference type or not.

The problem is that any nullable reference type T? is represented by the same type T (but with a compiler-generated attribute annotating it), as opposed to nullable value type T? that is represented by the actual .NET type Nullable<T>.

When compiler generates code that invokes a generic method F<T>, where T can be either nullable reference type or not, an information if T is nullable refence type is lost. Lets consider the next sample method:

public void F<T>(T value) { }

For the next invocations

F<string>("123");
F<string?>("456");

compiler will generate the next IL code (I simplified it a bit):

call    F<string>("123")
call F<string>("456")

You can see that to the second method a type parameter string is passed instead of string? because the representation of the nullable reference type string? during the execution is the same type string.

Therefore during execution it is impossible to define if a type parameter passed to a generic method is a nullable reference type or not.


I think that for your case an optimal solution would be to pass a bool value that will indicate if a reference type is nullable or not. Here is a sample, how it can be implemented:

public static Result<T> Create<T>(T value, bool isNullable = false)
{
Type t = typeof(T);

// If type "T" is a value type then we can check if it is nullable or not.
if (t.IsValueType)
{
if (Nullable.GetUnderlyingType(t) == null && value == null)
throw new ArgumentNullException(nameof(value));
}
// If type "T" is a reference type then we cannot check if it is nullable or not.
// In this case we rely on the value of the argument "isNullable".
else
{
if (!isNullable && value == null)
throw new ArgumentNullException(nameof(value));
}

...
}

C# Generic Method with non-nullable generic parameter warns of possible null reference

Someone could call:

var list = new List<object>();
M1<string?>(null, list);

Now your list, which should only contain non-nullable objects, contains null. Hence the warning.

If you want to prevent T from being a nullable type, then:

public void M1<T>(T t, List<object> l) where T : notnull
{
...
}

This gives you the warning:

M1<string?>(null, new List<object>());
^^^^^^^^^^^
// warning CS8714: The type 'string?' cannot be used as type parameter 'T' in the generic
// type or method 'C.M1<T>(T, List<object>)'. Nullability of type argument 'string?' doesn't
// match 'notnull' constraint.

If you want to let T be null, but still forbid null values for that t parameter, then:

public void M1<T>([DisallowNull] T t, List<object> l)
{
...
}

This gives you the warning:

M1<string?>(null, new List<object>());
^^^^
// warning CS8625: Cannot convert null literal to non-nullable reference type.

Of course, if you want to allow null then your l needs to be a List<object?>.



Related Topics



Leave a reply



Submit