为什么 "where T : object" 的类型约束不在 C# 8 中编译

Why isn't a type constraint of "where T : object" compiling in C# 8

根据 https://github.com/dotnet/csharplang/blob/master/proposals/csharp-8.0/nullable-reference-types-specification.md object 现在是一个有效的类型约束。

Nullable reference types can be used as generic constraints. Furthermore object is now valid as an explicit constraint. Absence of a constraint is now equivalent to an object? constraint (instead of object), but (unlike object before) object? is not prohibited as an explicit constraint.

我认为这应该可以正确编译,但 Visual Studio 不允许我编译它。

public class A<T> where T : object { }

我是误读了还是这是 VS 或 C#8 的问题?

更新

我正在尝试为缓存定义接口。

public interface IDataCache
{
    void Put<T>(string key, T value) where T : notnull;
    T? Get<T>(string key) where T : notnull;
}

理想情况下,我不希望缓存空值,但我也不能保证某个值会在缓存中。有时我想缓存一个 class 但其他时候它可能是一个整数。这有可能实现还是我必须为每种情况制定单独的方法?

public interface IDataCache
{
    void Put<T>(string key, T value) where T : notnull;

    // This seems really nasty!
    T? GetClass<T>(string key) where T : class;
    T? GetStruct<T>(string key) where T : struct;
}

这是一个有效的问题,答案不重要。由于正当理由,自最初提议以来,情况发生了很大变化。使用 object? 语法会导致其他地方出现混淆。

link 指向提案文档,而不是实际的文档或规范。您会发现 C# 指南的 Nullable Reference Types. There's no referecence to generic constraints there. The Constraints on type parameters 文章中的文档也没有提到 object?,但它确实提到了 notnull 约束。

更改及其背后的原因在 Try out Nullable Reference Types 中进行了解释。

常见情况:允许空值

这个:

public class A<T>
{
    T DoStuff(T input)
    {
        return input;
    }
}

接受任何结构或值类型,包括空类型。以下行不会生成任何警告:

var x=new A<string?>();

非空约束

您必须指定您想要具有 notnull 约束的不可空类型:


public class A<T>
    where T:notnull
{
    T DoStuff(T input)
    {
        return input;
    }
}

现在使用 string? 作为类型参数会产生警告:

warning CS8714: The type 'string?' cannot be used as type parameter 'T' in the generic type or method 'A'. Nullability of type argument 'string?' doesn't match 'notnull' constraint.

T的问题?

要使用 nullable 类型,您必须指定类型是 class 还是结构。其原因在介绍 NRT 的博客 post 的 The issue with T? section 中进行了解释。 T? 意味着 T 是不可空的,那么 T 是什么? Class 还是结构?编译器以不同方式处理每种情况。使用结构,编译器将生成 Nullable<T> 类型,而 classes 由编译器魔术处理。

此代码:

public class A<T>
{
    T? DoStuff(T input)
    {
        return input;
    }
}

将抛出编译器错误,而不仅仅是警告:

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.

添加 class 约束并传递 string 作为类型参数不会产生任何错误或警告:

public class A<T>
    where T:class
{
    T? DoStuff(T input)
    {
        return input;
    }
}

var x=new A<string>();