为什么我不能用T?作为 C# 中的泛型参数

Why I can't use T? as a generic parameter in C#

我在 .NET Core 控制台应用程序中使用 .NET 5。以下代码无法编译:

public class NullableClass<T?> 
{ 
}

错误是:

Error CS1003 Syntax error, ',' expected.

我是否使用任何约束都没有关系。

但是这段代码可以编译:

[return: MaybeNull]
public static T? Find<T>(IEnumerable<T?> sequence, Func<T?, bool> predicate)
{
    foreach (var element in sequence) {
        if (predicate(element)) return element;
    }
    return default(T?);
}

当我声明Find<T?>时我有同样的错误。
我在这里有什么不明白的地方?

如果您希望 T 成为可为 null 的引用类型,您应该为此使用 generic constraint

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.

public class NullableClass<T> where T : class?
{ 
    // code here
}

请注意,问号是约束的一部分 - class(没有问号)也有一个约束,意思是“引用类型”,在 c# 8 或更高版本中意思是“non-nullable 引用类型".

您对在哪里可以使用类型感到困惑。

您不应该在此处use/write输入

public class NullableClass<   >
                           ^^^

或此处:

public static T? Find<   >(IEnumerable<T?> sequence, Func<T?, bool> predicate)
                      ^^^

在句法上,这些地方是 type_parameters。您应该在那里声明类型参数,而不是使用现有类型。 public class Foo<List<int>> 没有意义,对吧?

根据语言规范,这是 class declaration:

的语法
class_declaration
    : attributes? class_modifier* 'partial'? 'class' identifier type_parameter_list?
      class_base? type_parameter_constraints_clause* class_body ';'?
    ;

type_parameter_list 是:

type_parameter_list
    : '<' type_parameters '>'
    ;

type_parameters
    : attributes? type_parameter
    | type_parameters ',' attributes? type_parameter
    ;

type_parameter
    : identifier
    ;

实际上,类型参数只是标识符,就像变量声明中的变量名一样。 T? 不是标识符。在 type_parameter_list 中,您只是声明此 class/method 将具有的类型参数。

现在让我们看看 使用的类型。

public static T? Find<T>(IEnumerable<T?> sequence, Func<T?, bool> predicate)
              ^^         ^^^^^^^^^^^^^^^           ^^^^^^^^^^^^^^

这些都是类型,所以你可以在那些地方使用T?。方法 return type 显然是一个类型,所以你可以在那里使用一个类型。我上面标记的<>是一个类型的一部分,所以它们是type_argument_list,而不是type_parameter_list。您可以在 type_argument_lists:

中使用类型
type_name
    : namespace_or_type_name
    ;

namespace_or_type_name
    : identifier type_argument_list?
    | namespace_or_type_name '.' identifier type_argument_list?
    | qualified_alias_member
    ;

type_argument_list
    : '<' type_arguments '>'
    ;

type_arguments
    : type_argument (',' type_argument)*
    ;

type_argument
    : type
    ;

如果要允许将可空引用类型和 non-nullable 引用类型用作类型参数 T 的类型参数,请使用 class? constraint

class NullableClass<T> where T: class? { ... }

Upvote Sweeper,如果你想使用可为 null 的类型,则不需要 ?关于定义,即

class Program
{
    static void Main(string[] args)
    {            
        int?[] valueList = new int?[] { 1, null, 3, 4, 5, null };
        int? val = NullableClass<int?>.Find(valueList, new Func<int?, bool>(test));
    }
    public static bool test(int? testMe)
    {
        // Do something productive here
        if (testMe != null && testMe % 2 == 0)
            return true;
        return false;
    }
}

class NullableClass<T> {
    public static T Find<T>(IEnumerable<T> sequence, Func<T, bool> predicate)
    {
        foreach (var element in sequence)
        {
            if (predicate(element)) return element;
        }
        return default(T);
    }
}