类型测试运算符和强制转换表达式 -
这些运算符和表达式执行类型检查或类型转换。
is
运算符检查表达式结果的运行时类型是否与给定类型兼容。
as
运算符将表达式显式转换为给定类型(如果其运行时类型与该类型兼容)。
强制转换表达式执行到目标类型的显式转换。
typeof
运算符获取某个类型的 System.Type 实例。
is
运算符
is
运算符检查表达式结果的运行时类型是否与给定类型兼容。
is
运算符还会对照某个模式测试表达式结果。
具有类型测试 is
运算符的表达式具有以下形式
E is T
其中 E
,一个表达式返回一个值,并且 T
是类型或类型参数的名称。
E
不得为匿名方法或 Lambda 表达式。
如果表达式结果为非 null 并且满足以下任一条件,则 is
运算符将返回 true
:
表达式结果的运行时类型会将标识转换为
T
。表达式结果的运行时类型是基础类型为 且
T
为 Nullable<T>.HasValue 的true
。
运算符 is
不考虑用户定义的转换或隐式跨度转换。
以下示例演示,如果表达式结果的运行时类型派生自给定类型,即类型之间存在引用转换,is
运算符将返回 true
:
public class Base { }
public class Derived : Base { }
public static class IsOperatorExample
{
public static void Main()
{
object b = new Base();
Console.WriteLine(b is Base); // output: True
Console.WriteLine(b is Derived); // output: False
object d = new Derived();
Console.WriteLine(d is Base); // output: True
Console.WriteLine(d is Derived); // output: True
}
}
以下示例演示,is
运算符将考虑装箱和取消装箱转换,但不会考虑数值转换:
int i = 27;
Console.WriteLine(i is System.IFormattable); // output: True
object iBoxed = i;
Console.WriteLine(iBoxed is int); // output: True
Console.WriteLine(iBoxed is long); // output: False
有模式匹配的类型测试
is
运算符还会对照某个模式测试表达式结果。 下面的示例演示如何使用声明模式来检查表达式的运行时类型:
int i = 23;
object iBoxed = i;
int? jNullable = 7;
if (iBoxed is int a && jNullable is int b)
{
Console.WriteLine(a + b); // output 30
}
若要了解受支持的模式,请参阅模式。
as
运算符
as
运算符将表达式结果显式转换为给定的引用或可以为 null 值的类型。 如果无法进行转换,则 as
运算符返回 null
。 与强制转换表达式 不同,as
运算符永远不会引发异常。
形式如下的表达式
E as T
其中 E
是返回一个值的表达式,而 T
是类型或类型参数的名称,生成的结果与
E is T ? (T)(E) : (T)null
除非E
只被评估一次。
as
运算符仅考虑引用、可以为 null、装箱和取消装箱转换。 不能使用 as
运算符执行用户定义的转换。 为此,请使用强制转换表达式。
下面的示例演示 as
运算符的用法:
IEnumerable<int> numbers = new List<int>(){10, 20, 30};
IList<int> indexable = numbers as IList<int>;
if (indexable != null)
{
Console.WriteLine(indexable[0] + indexable[indexable.Count - 1]); // output: 40
}
注意
如前面的示例所示,需要将表达式的结果 as
与 null
检查转换是否成功进行比较。 可以使用 is
运算符 来测试转换是否成功,如果成功,则将其结果分配给新变量。
强制转换表达式
形式为 (T)E
的强制转换表达式将表达式 E
的结果显式转换为类型 T
。 如果不存在从类型 E
到类型 T
的显式转换,则发生编译时错误。 在运行时,显式转换可能不会成功,强制转换表达式可能会引发异常。
下面的示例演示显式数值和引用转换:
double x = 1234.7;
int a = (int)x;
Console.WriteLine(a); // output: 1234
int[] ints = [10, 20, 30];
IEnumerable<int> numbers = ints;
IList<int> list = (IList<int>)numbers;
Console.WriteLine(list.Count); // output: 3
Console.WriteLine(list[1]); // output: 20
有关支持的显式转换的信息,请参阅 C# 语言规范的显式转换部分。 有关如何定义自定义显式或隐式类型转换的信息,请参阅用户定义转换运算符。
() 的其他用法
你还可以使用括号调用方法或调用委托。
括号的其他用法是调整表达式中计算操作的顺序。 有关详细信息,请参阅 C# 运算符。
typeof
运算符
typeof
运算符获取某个类型的 System.Type 实例。
typeof
运算符的实参必须是类型或类型形参的名称,如以下示例所示:
void PrintType<T>() => Console.WriteLine(typeof(T));
Console.WriteLine(typeof(List<string>));
PrintType<int>();
PrintType<System.Int32>();
PrintType<Dictionary<int, char>>();
// Output:
// System.Collections.Generic.List`1[System.String]
// System.Int32
// System.Int32
// System.Collections.Generic.Dictionary`2[System.Int32,System.Char]
参数不能是需要元数据注释的类型。 示例包括以下类型:
dynamic
-
string?
(或任何可为 null 的引用类型)
这些类型不会直接在元数据中表示出来。 这些类型包括描述基础类型的属性。 在这两种情况下,都可以使用基础类型。 可以使用 dynamic
来代替 object
。 可以使用 string?
来代替 string
。
你还可以使用具有未绑定泛型类型的 typeof
运算符。 未绑定泛型类型的名称必须包含适当数量的逗号,且此数量小于类型参数的数量。 以下示例演示了具有未绑定泛型类型的 typeof
运算符的用法:
Console.WriteLine(typeof(Dictionary<,>));
// Output:
// System.Collections.Generic.Dictionary`2[TKey,TValue]
表达式不能为 typeof
运算符的参数。 若要获取表达式结果的运行时类型的 System.Type 实例,请使用 Object.GetType 方法。
使用 typeof
运算符进行类型测试
使用 typeof
运算符来检查表达式结果的运行时类型是否与给定的类型完全匹配。 以下示例演示了使用 typeof
运算符和 is
运算符完成的类型检查之间的区别:
public class Animal { }
public class Giraffe : Animal { }
public static class TypeOfExample
{
public static void Main()
{
object b = new Giraffe();
Console.WriteLine(b is Animal); // output: True
Console.WriteLine(b.GetType() == typeof(Animal)); // output: False
Console.WriteLine(b is Giraffe); // output: True
Console.WriteLine(b.GetType() == typeof(Giraffe)); // output: True
}
}
运算符可重载性
is
、as
和 typeof
运算符无法进行重载。
用户定义的类型不能重载 ()
运算符,但可以定义由强制转换表达式执行的自定义类型转换。 有关详细信息,请参阅用户定义转换运算符。
C# 语言规范
有关更多信息,请参阅 C# 语言规范的以下部分: