类型测试运算符和强制转换表达式 - is
、as
、typeof
和强制转换
这些运算符和表达式执行类型检查或类型转换。 is
运算符检查表达式结果的运行时类型是否与给定类型兼容。 as
运算符将表达式显式转换为给定类型(如果其运行时类型与该类型兼容)。 强制转换表达式执行到目标类型的显式转换。 typeof
运算符用于获取某个类型的 System.Type 实例。
is 运算符
is
运算符检查表达式结果的运行时类型是否与给定类型兼容。 is
运算符还会对照某个模式测试表达式结果。
具有类型测试 is
运算符的表达式具有以下形式
E is T
其中 E
是返回一个值的表达式,T
是类型或类型参数的名称。 E
不得为匿名方法或 Lambda 表达式。
如果表达式结果为非 null 并且满足以下任一条件,则 is
运算符将返回 true
:
表达式结果的运行时类型为
T
。表达式结果的运行时类型派生自类型
T
、实现接口T
,或者存在从其到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 的引用类型)
这些类型不会直接在元数据中表示出来。 这些类型包括描述基础类型的属性。 在这两种情况下,都可以使用基础类型。 可以使用 object
来代替 dynamic
。 可以使用 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# 语言规范的以下部分: