【问题标题】:Why I can't use T? as a generic parameter in C#为什么我不能用T?作为 C# 中的泛型参数
【发布时间】:2020-11-08 05:46:09
【问题描述】:

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

public class NullableClass<T?> 
{ 
}

错误是:

错误 CS1003 语法错误,应为“,”。

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

但是这段代码可以编译:

[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&lt;T?&gt; 时,我遇到了同样的错误。
这里有什么不明白的地方?

【问题讨论】:

  • 代码class NullableClass&lt;T?&gt;是什么意思?
  • @Sweeper:我的意思是可以为空的引用类型,它们是在 c#9 中引入的
  • 您的意思是,您希望允许可空引用类型放入&lt;&gt;NullableClass 中?
  • @Sweeper:是的,我正在学习可空引用类型的用法,并且我试图理解为什么我不能做像 Find 这样的事情。我想了解为什么不允许这样做:是 C# 中的一些错误还是我确实错过了一些东西并且不明白。顺便说一句,错误信息看起来很奇怪,并没有直接限制它,它只是要求“,”,这很奇怪。
  • 吹毛求疵:可空引用类型是在 c# 8 而不是 9 中引入的。

标签: c# generics .net-core nullable-reference-types


【解决方案1】:

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

在哪里 T : 类?
类型参数必须是引用类型,可以为空或 不可为空。此约束也适用于任何类、接口、 委托,或数组类型。

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

请注意,问号是约束的一部分——class(没有问号)也有一个约束,意思是“引用类型”,在 c# 8 或更高版本中表示“不可为空的引用类型”。

【讨论】:

  • 谢谢。它有效,我尝试了两种变体: var nr = new NullableClass(); var sequence = new List() { null, null, "text here" }; var result = Find(sequence, x => x != null);它编译并正确返回序列中的最后一个元素。
  • 很高兴为您提供帮助 :-)
【解决方案2】:

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

你不应该在这里使用/写一个类型

public class NullableClass<   >
                           ^^^

或这里:

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

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

根据语言规范,这是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 中,您只是在声明此类/方法将具有哪些类型参数。

现在让我们看看类型在哪里使用

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

这些都是类型,所以你可以在那些地方使用T?。方法返回类型显然是一个类型,所以你可以在那里使用一个类型。我上面标记的&lt;&gt; 是一个类型的一部分,所以它们是type_argument_lists,而不是type_parameter_lists。您可以在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
    ;

如果您希望同时允许可空和不可空引用类型用作类型参数T 的类型参数,请使用class? constraint

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

【讨论】:

    【解决方案3】:

    点赞 Sweeper,如果你想使用可以为空的类型,你不需要 ?关于定义,即

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

    【讨论】:

    • OP 表示他想定义可为空的 reference 类型参数“我的意思是一个可为空的引用类型,它们被引入”
    猜你喜欢
    • 2020-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多