Nullable generic parameter C# [duplicate] - c#

Let's say we have some generic method with 2 parameters:
private void TestConstraints<TKey>(TKey key, TKey? nullableKey)
where TKey : notnull { }
That's perfectly fine from .NET6 perspective, where not nullable reference types were added. But using attempt is failing with reason CS8714 type can't be used as parameter:
private void TestMethod()
{
var id = 5;
int? nullableId = null;
TestConstraints(id, nullableId);
}
Warning CS8714 The type 'int?' cannot be used as type parameter 'TKey' in the generic type or method 'TestClass.TestConstraints(TKey, TKey?)'. Nullability of type argument 'int?' doesn't match 'notnull' constraint.
Specifying type explicitly as int does not help.
Could someone clarify if I could operate this way (defining input parameters as TKey?) or not, and where in documentation it is stated?
Compiler version:
Microsoft (R) Build Engine version 17.2.0+41abc5629

For unconstrained (neither to struct nor to class) generic type parameter T - T? is handled differently for value types and reference types. From the docs:
If the type argument for T is a reference type, T? references the corresponding nullable reference type. For example, if T is a string, then T? is a string?.
If the type argument for T is a value type, T? references the same value type, T. For example, if T is an int, the T? is also an int.
If the type argument for T is a nullable reference type, T? references that same nullable reference type. For example, if T is a string?, then T? is also a string?.
If the type argument for T is a nullable value type, T? references that same nullable value type. For example, if T is a int?, then T? is also a int?.
This happens partially due to the differences in how nullable reference and nullable value types are handled, cause nullable value types are actually separate types - Nullable<T>.
For TestConstraints let's imagine that T is int then according to the rules the signature becomes TestConstraints<int>(int key, int nullableKey) which obviously will not compile for the TestConstraints<int>(id, nullableId) call due to the type mismatch (TestConstraints<int>(id, nullableId.Value) will compile but throw at the runtime).
For int? (Nullable<int>) the signature becomes TestConstraints<int?>(int? key, int? nullableKey) which will compile (due to the implicit conversion T -> Nullable<T>) but obviously will fail the generic constraint with the warning.
The workaround can be to introduce two overloads, one for struct, one for class and let the compiler figure it out:
private void TestConstraints<TKey>(TKey key, TKey? nullableKey)
where TKey : struct
{ }
private void TestConstraints<TKey>(TKey key, TKey? nullableKey)
where TKey : class
{ }

Related

Are nullable reference types not Nullable<T>?

I created a new C#10/.net6 library project in VS2022 using the standard template, NRTs are enabled by default (nullable : enabled). I notice that nullable reference types do not appear to support Nullable properties Value and HasValue
MRE (using #nullable annotation explicitly)
using System;
public class Program
{
public static void Main()
{
#nullable enable
object? o =null;
if(o.HasValue)
{}
Console.WriteLine("Hello World");
}
}
'object' does not contain a definition for 'HasValue' and no
accessible extension method 'HasValue' accepting a first argument of
type 'object' could be found
Are nullable reference types not actually Nullable<T>? o is declared as object? so why does the compiler refer to object rather than object??
No, they are not. From the docs:
However, nullable reference types and nullable value types are implemented differently: nullable value types are implemented using System.Nullable<T>, and nullable reference types are implemented by attributes read by the compiler. For example, string? and string are both represented by the same type: System.String. However, int? and int are represented by System.Nullable<System.Int32> and System.Int32, respectively.
Basically NRT is just compile time information (though you can get access it in runtime via reflection like done here).
As for the case - just use ordinary null check and that's it.
if(o != null)
{
}
or pattern matching:
if(o is {} notNullO)
{
}

The type 'T1' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable<T>'

Here iam getting error
The type 'string' must be a non-nullable value type in order to use it as parameter 'T1' in the generic type or method 'GetDataKeyValue(System.Web.UI.WebControls.GridView, int, string)'
String Process = GetDataKeyValue<String>(gvTargetRate, RowIndex, "Process");
Here iam getting error
The type 'T1' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable'
private Nullable<T> GetDataKeyValue<T>(GridView gv, int RowIndex, T column)
{
if (column == null)
return default(T);
return (T)gv.DataKeys[RowIndex].Values[column];
}
You can't create a Nullable<> out of any type. As the compiler tells you, you can only do that for value types. Since the method isn't restricted to any type, it can't say for sure you won't pass it, let's say, an object.
What you want isn't possible, actually, as explained in this question and its answer.
And by the way, T isn't a nullable type by definition, so default(T) will become a non-null value in the case of value types. string is on object, so it can be null without the use of Nullable<T>.
The Nullable type requires that T is a non-nullable value type, for example int or DateTime. Reference types like string can already be null. There would be no point in allowing things like Nullable so it is disallowed.
more info.

Why string or object type don't support nullable reference type? [duplicate]

This question already has answers here:
C# nullable string error
(5 answers)
Closed 8 years ago.
Look into following code block:
//Declaring nullable variables.
//Valid for int, char, long...
Nullable<int> _intVar;
Nullable<char> _charVar;
//trying to declare nullable string/object variables
//gives compile time error.
Nullable<string> _stringVar;
Nullable<object> _objVar;
While compiling code compiler gives following error message:
The type 'string'/'object' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable'
I read it several times but still unable to understand. Can anyone clarify this? Why object or string dont support nullable reference type?
object and string are reference types, so they're already nullable. For example, this is already valid:
string x = null;
The Nullable<T> generic type is only for cases where T is a non-nullable value type.
In the declaration for Nullable<T> there is a constraint on T:
public struct Nullable<T> where T : struct
That where T : struct is precisely the part that constrains T to be a non-nullable value type.
Nullable<T> is defined as:
public struct Nullable<T> where T : struct
meaning: it only works on value-type T (excluding Nullable<TSomethingElse> itself).
You cannot use Nullable<T> on reference-types (or on Nullable<T>), but you don't need to since all reference-types (including object and string) are already "nullable", in that you can assign null to them.
string and object are reference types, and therefore are "nullable" already. The Nullable<T> type exists as a wrapper around value types that don't support null out of the box.
string myString = null //fine
int myInt = null //compiler error

What C# data types can be nullable types?

Can someone give me a list, or point me to where I can find a list of C# data types that can be a nullable type?
For example:
I know that Nullable<int> is ok
I know that Nullable<byte[]> is not.
I'd like to know which types are nullable and which are not. BTW, I know I can test for this at runtime. However, this is for a code generator we're writing, so I don't have an actual type. I just know that a column is string or int32 (etc).
All value types (except Nullable<T> itself) can be used in nullable types – i.e. all types that derive from System.ValueType (that also includes enums!).
The reason for this is that Nullable is declared something like this:
struct Nullable<T> where T : struct, new() { … }
A type is said to be nullable if it can be assigned a value or can be assigned null, which means the type has no value whatsoever. Consequently, a nullable type can express a value, or that no value exists. For example, a reference type such as String is nullable, whereas a value type such as Int32 is not. A value type cannot be nullable because it has enough capacity to express only the values appropriate for that type; it does not have the additional capacity required to express a value of null.
The Nullable structure supports using only a value type as a nullable type because reference types are nullable by design.
The Nullable class provides complementary support for the Nullable structure. The Nullable class supports obtaining the underlying type of a nullable type, and comparison and equality operations on pairs of nullable types whose underlying value type does not support generic comparison and equality operations.
From Help Docs
http://msdn.microsoft.com/en-us/library/b3h38hb0.aspx
It can be any value type including struct, it cannot be a reference type, as those are inherently nullable already.
Yes:
Int32
double
DateTime
CustomStruct
etc.
No:
string
Array
CustomClass
etc.
For more information, see MSDN: http://msdn.microsoft.com/en-us/library/2cf62fcy(v=VS.80).aspx
Any data type can be nullable.
Nullable works for you because normal int is not nullable, so the conversion to nullable int is necessary.
Nullable<int[]> not works because int[] is already a nullable type.
Nullable does not support types that are already nullable.
Note, that you need to convert a type to be nullable only if he is not already a nullable type.
Value types are not nullable, value types are int, float, double, long etc, as well as structs you create.
While reference types, for example, are already nullable (classes for example).
You can use the ? operator to make the type to be a nullable type.
Note, that even tho it's not a good practice if you would use the question mark operator on a type that is already nullable it will not raise an error.
int? myNullableVar = null;
int[]? MyNullableArr = null; //not necessary but does not raise an error

Do C# Nullable variables still function as value types?

If I declare a nullable (either via Nullable or the ? symbol) variable from a value type, does it still respect the rules of value types (i.e. pass by value by default, deallocated when it falls out of scope, not when the garbage collector runs) or does it turn into a reference type since it's actually of type Nullable?
The documentation is here:
http://msdn.microsoft.com/en-us/library/b3h38hb0.aspx
As you can see, the documentation describes this as the "nullable structure", indicating that it is a value type. Also, the documentation gives the first lines of the declaration of the type:
public struct Nullable<T> where T : struct, new()
again showing that the type is a value type.
Yes, System.Nullable is a generic Struct, ie value type.
Nullable value types are essentially just a struct wrapper around a value type which allows them to be flagged as null.
something like this:
struct Nullable<T>
{
public T Value;
public bool HasValue;
}
I believe it's actually more complex than that under the hood, and the compiler does lots of nice stuff for you so you can write for example if(myInt == null). But yes, they are still value types.

Categories