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
Is There a Reason Image.Fromfile Throws an Outofmemoryexception for an Invalid Image Format
An Object Reference Is Required to Access Non-Static Member
Get the Serial Number of Usb Storage Devices in .Net Core 2.1
How to Remove Whitespace on Merge
SQL Command Insert Is Working But the Data Not Appear in Table
Benchmarking Small Code Samples in C#, Can This Implementation Be Improved
Resharper "Cannot Resolve Symbol" Even When Project Builds
Marshal C++ Struct Array into C#
Naming Convention - Underscore in C++ and C# Variables
Kill Some Processes by .Exe File Name
Howto Implement Callback Interface from Unmanaged Dll to .Net App
A Call to Pinvoke Function '[...]' Has Unbalanced the Stack