`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
。
foo is T bar
运算符测试 非 null
值 的类型。如果 foo
是 null
则 is
表达式 returns false 并且 bar
是 default(T)
.
-
T bar = (T)foo
转换表达式 将转换 null
值 但前提是 T
是引用类型 - 但此语法也可以调用自定义显式转换运算符而不是执行转换。
另请注意,C# 8.0 的可空引用类型功能 (object?
) 在运行时未强制执行:,因此如果您更改代码以使用 object
而不是 object?
你仍然应该添加 if( val is null ) throw new ArgumentNullException( nameof(val) );
.
为什么第一个函数通过编译而第二个函数发出警告?这两个实现不等价吗? 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
。
foo is T bar
运算符测试 非null
值 的类型。如果foo
是null
则is
表达式 returns false 并且bar
是default(T)
.-
T bar = (T)foo
转换表达式 将转换null
值 但前提是T
是引用类型 - 但此语法也可以调用自定义显式转换运算符而不是执行转换。
另请注意,C# 8.0 的可空引用类型功能 (object?
) 在运行时未强制执行:object
而不是 object?
你仍然应该添加 if( val is null ) throw new ArgumentNullException( nameof(val) );
.