`is` 运算符和转换为泛型类型有什么区别?

What is the difference between the `is` operator and a cast to a generic type?

为什么第一个函数通过编译而第二个函数发出警告?这两个实现不等价吗? valT 的类型在两种实现中都是 T

https://dotnetfiddle.net/XV5AXU

T To1<T>(object? val)
{
  if (!(val is T valT))
  {
    throw new InvalidCastException();
  }

  return valT;
}

T To2<T>(object? val)
{
  var valT = (T)val;

  return valT; // Possible null reference return
}

前言:您可能不需要实现通用的“Cast”方法。在 C# 中实现泛型方法以提高性能时要小心,避免装箱(避免使用 object)。此外,如果您在泛型方法中隐藏实际的显式转换运算符,则 C# 编译器无法向使用代码提供转换可证明不正确的早期警告

考虑:

Int32 i = new Int32();
String s = (String)o; // <-- CS0030 Cannot convert type 'int' to 'string'

但是使用您的代码:

Int32 i = new Int32();
String s1 = To1<String>( i ); // No compiler error, despite incompatible types.
String s2 = To2<String>( i ); // Also no compiler error.

关于您的问题:

Are the two implementations not equivalent?

没有。看看当你通过 val: null:

时会发生什么
// Using your functions `To1` and `To2`:

String strFromObj  = To1<String>( new Object() ); // throws InvalidCastException (thrown from your code)
String strFromNull = To1<String>( null         ); // throws InvalidCastException (thrown from your code)

String strFromObj  = To2<String>( new Object() ); // throws InvalidCastException (thrown from the operator)
String strFromNull = To2<String>( null         ); // returns null;

Why does the first function compile and the second emit a warning?

  • 你的第一个函数使用了is运算符,它是安全的并且永远不会抛出(注意它是你的代码 在这种情况下抛出 InvalidCastException)。
  • 你的第二个函数 盲目地 使用强制转换表达式而不先进行类型检查,所以它是不安全的,因为如果 T 是,运算符本身会抛出 InvalidCastException不正确,除非操作数是null

  1. foo is T bar 运算符测试 null 的类型。如果 foonullis 表达式 returns false 并且 bardefault(T).
  2. T bar = (T)foo 转换表达式 将转换 null 但前提是 T 是引用类型 - 但此语法也可以调用自定义显式转换运算符而不是执行转换。

另请注意,C# 8.0 的可空引用类型功能 (object?) 在运行时未强制执行:,因此如果您更改代码以使用 object而不是 object? 你仍然应该添加 if( val is null ) throw new ArgumentNullException( nameof(val) );.