如何在 Dart NNBD 中检查通用类型是否可为空?

17
假设我有一个函数,它以泛型类型作为参数。在该函数内部,如何检查泛型类型参数是否可为空?我想要像这样做:
void func<T>() {
  print(T is nullable);
}

void main(){
  func<int>(); //prints false
  func<int?>(); //prints true
}

我能想到的唯一方法就是检查T.toString()是否以?结尾,这种方法非常不正规。


我进行了研究,但没有发现任何有用的信息... 我认为你可以尝试使用扩展方法来扩展基本内置对象类... - pedro pimont
1
我认为如果你问“是对象吗?”,那只有可空类型才会是真的。 - Randal Schwartz
@RandalSchwartz T is Object? 不起作用;is需要一个实例。T == Object?不是合法的语法(而且==对于派生类型也不起作用)。instance is Object?也不起作用,因为它总是真的。instance is! Object可能有点用,但我认为这并不比instance != null更好;它并没有告诉你关于参数化类型本身的任何信息。 - jamesdlin
@JonAird 你为什么想要这样的检查呢?你应该让你的通用接受 T? 并假设它在任何地方都是可空的,或者你应该通过添加一个 T extends Object 约束来要求非空类型。另请参见 https://github.com/dart-lang/language/issues/143。 - jamesdlin
例如,在使用默认列表行为实现ListMixin时,必须实现length setter。如果新长度大于当前长度,则列表的长度将扩展,并将新元素设置为“null”。希望列表实现能够接受可空或非可空类型。 - Jon Aird
我认为另一个问题中的这个答案也很有价值。https://dev59.com/H8Dqa4cB1Zd3GeqPhZtE#67448929 - Hyukjoong Kim
5个回答

38

尝试:

bool isNullable<T>() => null is T;

3
接受的答案仅检查类型是否可为空。它不关心您在空运算符上操作的类型。
如果您想检查类型是否为特定的可空类型,即如果您想检查类型是否特定为 DateTime?而不是 String?,则无法通过T == DateTime?在dart中执行此操作,因为这与三元运算符语法冲突。
但是,由于dart允许将可空类型传递给泛型参数,因此可以像这样进行操作:
bool isType<T, Y>() => T == Y;

isType<T, DateTime?>() 能够正常使用。


1
将这样的函数命名为 isType 可能会产生误导,因为 "is" 可能暗示着一个 "is-a" 检查而不是相等性检查。将其命名为 equalsType 将更清晰,或者创建一个更通用的 Type typeIdentity<T>() => T 函数,然后执行 T == typeIdentity<DateTime?>() - jamesdlin

2

我在类型T上创建了一个扩展方法来检查是否为空。

extension _Generic<T extends Object?> on T? {
  bool get isNullable {
    try {
      // throws an exception if T is not nullable
      final value = null as T;
      return true;
    } catch (_) {
      return false;
    }
  }
}

运行这些检查全部通过

void main() {
  print('hello world'.isNullable); // false
  print(1.isNullable); // false
  print(1.0.isNullable); // false
  print(true.isNullable); // false
  print([].isNullable); // false
  print({}.isNullable); // false
  print(null.isNullable); // true
  print(null.runtimeType.isNullable); // false, because `runtimeType` is not nullable
}

"@Irn 的方法效果不错,除非 TType 类型。咦?T类型总是 Type。你能提供一个具体的示例,说明在哪种情况下需要这种方法吗?此外,按照现有代码,它无法作为自由函数使用,从哪里获取 T 呢?(最后,您指的是 lrn(小写字母 L),而不是 Irn。他是 Dart 语言团队的成员 Lasse Nielsen。)" - jamesdlin
这是一个泛型类型 T 的扩展方法,这就是它的来源。在运行时,T 不是 Type 类型,除非类型参数是字面关键字 Type。"Dart 泛型类型是具体化的,这意味着它们在运行时携带它们的类型信息" - dart 泛型文档。老实说,我回答这个问题已经有一段时间了,我无法复制一个示例来证明我的答案。抱歉 @lrn 的句柄不正确。l 和 I 看起来完全一样,我不是第一个这样做的人,也不会是最后一个。我相信他一直都遇到这种情况。对于可能造成的任何混淆,我感到很抱歉。 - mrgnhnt96
你最初的说法没有意义,因为 T类型 应该始终是 Type。也就是说,T is Type 应该为真(或 T.staticType == Type 应该为真,或者 T.runtimeType 应该类似于 Type_Type)。T 通常会是其他东西(例如 intString? 等)。 - jamesdlin

1

它使用支持继承的is运算符创建一个新的列表实例来验证其类型是否可为空:

bool isNullable<T>() => <T?>[] is List<T>;

1
我并没有看到这种方法比使用null is T的方法更有优势,但这仍然是一个相当聪明的技巧,可以更普遍地用于检查泛型的类型参数是否派生自另一个类型。 - jamesdlin

0
这是我迄今为止找到的唯一可靠的方法:
bool isType<T>(Type t) => t == T;
bool isNullableType<T>() => isType<T?>(T);

class X<Y> {
  void testNullable() => print(isNullableType<Y>());
}

void main() {
  X<int>().testNullable();   // false
  X<int?>().testNullable();  // true
  X().testNullable();        // true because `dynamic` is nullable
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接