C# 返回类型中引用类型的可空性与重写的成员不匹配

C# 返回类型中引用类型的可空性与重写的成员不匹配,c#,.net-core,.net-core-3.0,c#-8.0,nullable-reference-types,C#,.net Core,.net Core 3.0,C# 8.0,Nullable Reference Types,我使用的API返回JSON,其中一个值可以是false,也可以是object。为了解决这个问题,我创建了一个自定义 内部类JsonFalseOrObjectConverter:JsonConverter,其中T:class { public override T Readref Utf8JsonReader reader,键入typeToConvert,JsonSerializerOptions选项 { 如果reader.TokenType==JsonTokenType.False { 返回nu

我使用的API返回JSON,其中一个值可以是false,也可以是object。为了解决这个问题,我创建了一个自定义

内部类JsonFalseOrObjectConverter:JsonConverter,其中T:class { public override T Readref Utf8JsonReader reader,键入typeToConvert,JsonSerializerOptions选项 { 如果reader.TokenType==JsonTokenType.False { 返回null; } 其他的 { 返回JsonSerializer.Deserializeref读取器; } } } 问题是我遇到以下编译器错误:

可能的空引用返回

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

返回类型中引用类型的可空性与重写的成员不匹配


如何修复此问题?

您已经声明泛型类型是不可为null的T,但您正在返回null。这显然是无效的

您需要让转换器实现JsonConverter,如果您不在乎的话,还需要使用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)
    {
        ...
    }
}

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

您需要让转换器实现JsonConverter,如果您不在乎的话,还需要使用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)
    {
        ...
    }
}

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

如果遇到false,反序列化程序可以返回None或default:


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;
        }
    }
}    
可序列化为false,为缺少的类别生成:

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

var product1=new Product{Name="A"};
var json=JsonSerializer.Serialize(product1,serializerOptions);
反序列化此字符串将返回其类别为无值选项的产品:

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};


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

如果遇到false,反序列化程序可以返回None或default:


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;
        }
    }
}    
可序列化为false,为缺少的类别生成:

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

var product1=new Product{Name="A"};
var json=JsonSerializer.Serialize(product1,serializerOptions);
反序列化此字符串将返回其类别为无值选项的产品:

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};


引用类型本质上是可以为空的。只有通常作为结构实现的值类型才需要显式指定。错误消息告诉您不允许从此函数返回null,因为您要重写的函数不允许返回null值。我想我们需要看到您在这里重写的函数。我应该指定我已经启用了C8中的可空引用类型。我要重写的方法是你应该重新思考你在做什么。无论使用哪种转换器,为同一属性返回任意值的API都会使每个客户机感到困惑。false也是一个非常特定的JSON值,而不是null或缺少的值。如果无法更改API,请考虑返回一个新的TimTy实例而不是NULL。您可以使用一个选项类型,如所描述的或与模式匹配相结合的选项类型,当属性为FalSereDebug类型固有可空时,不返回任何选项。只有通常作为结构实现的值类型才需要显式指定。错误消息告诉您不允许从此函数返回null,因为您要重写的函数不允许返回null值。我想我们需要看到您在这里重写的函数。我应该指定我已经启用了C8中的可空引用类型。我要重写的方法是你应该重新思考你在做什么。无论使用哪种转换器,为同一属性返回任意值的API都会使每个客户机感到困惑。false也是一个非常特定的JSON值,而不是null或缺少的值。如果你不能改变API,考虑返回一个新的TimTy实例而不是NULL。你可以使用一个选项类型,如描述的或者与模式匹配相结合的,当属性是假的时候,不返回任何一个解决方案,但是我真的很困惑。第一个,不是吗?等效于Nullable,它是一个Nullable结构?如何区分可为null的ref和可为null的struct?为什么不呢?这里允许,但这里不允许JsonFalseOrConverter?对于第二个问题,编译器如何允许对文本null进行null原谅。运算符的目的不是告诉编译器不要担心它不是null.t吗?仅等效于可为null的iff T是值类型。在您的示例中,它被约束为引用类型。没有为可空引用类型创建特殊类型,编译器只是为您执行额外的检查,以确保引用这些类型的任何代码与其可空性一致。一般来说,您将无法在可空值类型和可空引用类型之间共享相同的泛型类,编译器将不得不生成单独的类
erator允许您告诉编译器,即使预期的类型是不可为null的引用,您也表示希望在此处使用可为null的值,编译器不应对此发出警告。在幕后,一切仍然是相同的引用类型,只是编译器对如何使用它有或多或少的严格要求。两种解决方案都很好,但我真的很困惑。第一个,不是吗?等效于Nullable,它是一个Nullable结构?如何区分可为null的ref和可为null的struct?为什么不呢?这里允许,但这里不允许JsonFalseOrConverter?对于第二个问题,编译器如何允许对文本null进行null原谅。运算符的目的不是告诉编译器不要担心它不是null.t吗?仅等效于可为null的iff T是值类型。在您的示例中,它被约束为引用类型。没有为可空引用类型创建特殊类型,编译器只是为您执行额外的检查,以确保引用这些类型的任何代码与其可空性一致。一般来说,您将无法在可空值类型和可空引用类型之间共享同一泛型类,编译器仍必须生成单独的类。允许您使用null原谅运算符告诉编译器,即使预期的类型是不可空引用,您的意思是希望在这里使用一个可为null的值,而编译器不应该对此发出警告。在幕后,一切仍然是相同的引用类型,只是编译器或多或少地严格要求您如何使用它。
if(product2.Category is Option<Category>(var cat,true))
{
    Console.WriteLine(cat.Name);
}