return 类型中引用类型的可空性与覆盖的成员不匹配

Nullability of reference types in return type doesn't match overridden member

我正在使用 API 和 returns JSON,其中一个值可以是 false 或一个对象。为了处理这个问题,我创建了一个自定义 JsonConverter<T>.

internal class JsonFalseOrObjectConverter<T> : JsonConverter<T> where T : class
{
    public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType == JsonTokenType.False)
        {
            return null;
        }
        else
        {
            return JsonSerializer.Deserialize<T>(ref reader);
        }
    }
}

问题是我收到以下编译器错误:

Possible null reference return.

我可以将返回的类型设置为 T? 但是我会得到错误:

Nullability of reference types in return type doesn't match overridden member.

我该如何解决?

您已声明泛型类型是(不可为空的)T,但您要返回 null。这显然是无效的。

如果您不在乎,则需要让转换器实现 JsonConverter<T?> 或使用 null 宽容运算符。

internal class JsonFalseOrObjectConverter<T> : JsonConverter<T?> where T : class
{
    public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        ...
    }

    public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options)
    {
        ...
    }
}

public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
    if (reader.TokenType == JsonTokenType.False)
    {
        return null!;
    }
    ...
}

最简单的解决方案是 return null! :

#nullable enable

internal class JsonFalseOrObjectConverter<T> : JsonConverter<T> where T : class
{
    public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType == JsonTokenType.False)
        {
            return null!;
        }
        else
        {
            return JsonSerializer.Deserialize<T>(ref reader);
        }
    }

    public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options){}
}    

引用 类 可以为空,因此编译器在遇到 T? 时只使用 T

更好的选择是创建类似于 F# 的 Option type 的内容,如果设置了值,则包含 Some value,如果值为 false,则包含 None。通过使 Option 成为一个结构,即使 属性 缺失或为空,我们也会得到默认的 None 值:

readonly struct Option<T> 
{
    public readonly T Value {get;}

    public readonly bool IsSome {get;}
    public readonly bool IsNone =>!IsSome;

    public Option(T value)=>(Value,IsSome)=(value,true);    

    public void Deconstruct(out T value)=>(value)=(Value);
}

//Convenience methods, similar to F#'s Option module
static class Option
{
    public static Option<T> Some<T>(T value)=>new Option<T>(value);    
    public static Option<T> None<T>()=>default;
}

解串器可以return None() 或者default 如果遇到false:


internal class JsonFalseOrObjectConverter<T> : JsonConverter<Option<T>> where T : class
{
    public override Option<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType == JsonTokenType.False)
        {
            return Option.None<T>(); // or default
        }
        else
        {
            return Option.Some(JsonSerializer.Deserialize<T>(ref reader));
        }
    }

    public override void Write(Utf8JsonWriter writer, Option<T> value, JsonSerializerOptions options)
    {
        switch (value)
        {
            case Option<T> (_    ,false) :
                JsonSerializer.Serialize(writer,false,options);
                break;
            case Option<T> (var v,true) :
                JsonSerializer.Serialize(writer,v,options);
                break;
        }
    }
}    

Write 方法展示了如何使用模式匹配处理 Option<T>

使用此序列化程序,以下 类 :


class Category
{
    public string Name{get;set;}
}


class Product
{
    public string Name{get;set;}

    public Option<Category> Category {get;set;}
}

可以用 false 为缺失的类别生成序列化:

var serializerOptions = new JsonSerializerOptions
{ 
    Converters = { new JsonFalseOrObjectConverter<Category>() }
};

var product1=new Product{Name="A"};
var json=JsonSerializer.Serialize(product1,serializerOptions);

这 returns :

{"Name":"A","Category":false}

反序列化此字符串 returns 一个 ProductCategory 是一个没有值的 Option<Category> :

var product2=JsonSerializer.Deserialize<Product>(json,serializerOptions);
Debug.Assert(product2.Category.IsNone);

模式匹配表达式可用于提取和使用具有值的类别的属性,例如:

string category=product2.Category switch { Option<Category> (_    ,false) =>"No Category",
                                        Option<Category> (var v,true)  => v.Name};

if(product2.Category is Option<Category>(var cat,true))
{
    Console.WriteLine(cat.Name);
}