C# TypeKind of string? is TypeKind.Class

Viewed 99

Rephrased question: I have this equalitycomparer with Generic type constrained to a class

public class ReferenceEqualityComparer<T> : IEqualityComparer<T> where T : class
{
    public static ReferenceEqualityComparer<T> Default => new();

    public bool Equals(T? x, T? y) => ReferenceEquals(x, y);

    public int GetHashCode(T? obj) => RuntimeHelpers.GetHashCode(obj);
}

when a class let's say

public class A
{
   public string? P1{get; set;}
}

is consumed by a code generator

[Generator]
public class MyCodeGenerator : ISourceGenerator
{
}

the NetAnalyzer says that string? has a TypeKind of class

but when I do this,

#nullable enable        
[TestMethod]
public void RefTest()
{
    string? s1 = "adsad";
    string? s3 = s1;
    Assert.IsTrue(ReferenceEqualityComparer<string?>.Default.Equals(s1, s3));
}
#nullable restore

it says that string?does not match 'class' constraint. Even though the analyzer is telling me that its a class, am I missing something here? or have I misunderstood the concept?

Original question: According to the description of Nullable from Microsoft Documentation, they are classified as structs, but why is it that the CodeAnalyzer is telling me that the TypeKind of string? is a TypeKind.Class?

Here's some context, in a library I'm writing, classes are analyzed for Source Generation (C# 9 Source Generator) which is essentially using .NetAnalyzer. Each of the properties of the class will be checked whether their type is considered as a class. It turns out string? is considered as a Class.

2 Answers

There is a specific constraint for nullable classes in C#, so from constraint standpoint in compile time SomeRefType? does not match T:class, but it will match T:class?.

where T : class The type argument must be a reference type. This constraint applies also to any class, interface, delegate, or array type. In a nullable context in C# 8.0 or later, T must be a non-nullable reference type.

where T : class? The type argument must be a reference type, either nullable or non-nullable. This constraint applies also to any class, interface, delegate, or array type.

So possibly in nullable context you will want to use the second one:

public class ReferenceEqualityComparer<T> : IEqualityComparer<T> where T : class?

Which will not give any warnings neither for string? nor for string.

strings are classes and cannot be used as generic type argument for Nullable because of the generic constraint where T : struct. In your context string? is a nullable reference type, which can be used to incdicate to the compiler that a reference type should not have null as value (variables still can have null as value and should be checked for null values in public APIs, but the compiler will warn you when you use null in a non-nullable context)

Related