Receiving error about nullable type parameter even when parameter has notnull constraint

泄露秘密 提交于 2020-01-24 16:38:07

问题


I have a generic interface IDataAdapter<T>; implementors of the interface should be able to read POCOs with Guid IDs from a data source. IDataAdapter<T> has a method Read(Guid id) which I want to return a T?, where null indicates that no matches were found in the data source. However, even with a constraint T : notnull on IDataAdapter<T>, attempting to define this method gives the error CS8627: A nullable type parameter must be known to be a value type or non-nullable reference type. Consider adding a 'class', 'struct', or type constraint. Why am I still getting this error, even with T constrained to notnull?

Code (should be in a C# 8 environment with <Nullable>enable</Nullable>):

interface IDataAdapter<T> where T : notnull
{
    T? Read (Guid id); // error CS8627
}

回答1:


I think this issue is very similar to what is happening in this post.

Note that a T? where T : class and a T? where T : struct are represented very differently in the CLR. The former is just the CLR type T. There are not separate types in the CLR to differentiate between T and T?. T? in C# just adds extra compile time checking by the C# compiler. On the other hand, The latter is represented by the CLR type Nullable<T>.

So let's consider your method:

T? Read (Guid id);

How should this be represented in the CLR? What is the return type? The compiler don't know whether T is a reference type or a value type, so the compiler cannot decide whether the method signature should be:

T Read (Guid id);

or:

Nullable<T> Read (Guid id);



回答2:


If you look at Nullable Struct's documentation you can see that it requires to be a struct:

public struct Nullable<T> where T : struct

I believe you will need to constraint T to be a struct:

interface IA<T> where T : struct
{
    T? Read(Guid id);
    // Or Nullable<T> Read(Guid id);
}


class A : IA<int>
{
    public int? Read(Guid id) { Console.WriteLine("A"); return 0; }
}

BTW. Could you give us an example of what types you want to use this class with?

Why not just use where T: class and return T (or even not have a constraint at all)?

interface IA<T> where T : class
{
    T Read(Guid id);
}



回答3:


The same error is raised if you don't use the notnull constraint. You need to specify what that type is with a class or struct constraint. You don't need to specify notnull as structs were always nullable and, with nullable ref types enabled, so are classes.

Just add where T:class or where T:struct.

Reference Types

If you add the class constraint, eg:

#nullable enable

interface IDataAdapter<T>       
    where T:class
{
    T? Read (Guid id); // error CS8627

    void Something(T input);
}

class StringAdapter:IDataAdapter<string>
{
    public string Read(Guid id)=>id.ToString();

    public void Something(string input){}
}

The following call will generate a warning:

var adp=new StringAdapter();
string? x=null;
adp.Something(x);  //CS8604: Possible null reference argument ....

Value Types

Using struct to create an IntAdapter on the other hand results in a compilation error if the argument is nullable :

interface IDataAdapter<T>       
    where T:struct
{
    T? Read (Guid id); // error CS8627

    void Something(T input);
}


class IntAdapter:IDataAdapter<int>
{
    public int? Read(Guid id)=>id.ToString().Length;

    public void Something(int input){}
}

void Main()
{

    var adp=new IntAdapter();
    int? x=null;
    adp.Something(x);  //CS1503: Cannot convert from int? to int
}

That's because the compile generated methods that expect an int? instead of an int.

Explanation

The reason is that the compiler has to generate very different code in each case. For a class, it doesn't have to do anything special. For a struct, it has to generate a Nullable< T>.

This is explained in the The issue with T? section in Try out Nullable Reference Types :

This distinction between nullable value types and nullable reference types comes up in a pattern such as this:

void M<T>(T? t) where T: notnull

This would mean that the parameter is the nullable version of T, and T is constrained to be notnull. If T were a string, then the actual signature of M would be M([NullableAttribute] T t), but if T were an int, then M would be M(Nullable t). These two signatures are fundamentally different, and this difference is not reconcilable.

Because of this issue between the concrete representations of nullable reference types and nullable value types, any use of T? must also require you to constrain the T to be either class or struct.



来源:https://stackoverflow.com/questions/58229782/receiving-error-about-nullable-type-parameter-even-when-parameter-has-notnull-co

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!