类型测试运算符和强制转换表达式(C# 引用)

可以使用以下运算符和表达式来执行类型检查或类型转换:

is 运算符

is 运算符检查表达式结果的运行时类型是否与给定类型兼容。 is 运算符还会对照某个模式测试表达式结果。

具有类型测试 is 运算符的表达式具有以下形式

E is T

其中 E 是返回一个值的表达式,T 是类型或类型参数的名称。 E 不得为匿名方法或 Lambda 表达式。

如果表达式结果为非 null 并且满足以下任一条件,则 is 运算符将返回 true

  • 表达式结果的运行时类型为 T

  • 表达式结果的运行时类型派生自类型 T、实现接口 T,或者存在从其到 T 的另一种隐式引用转换

  • 表达式结果的运行时类型是基础类型为 TNullable<T>.HasValuetrue可为空值类型

  • 存在从表达式结果的运行时类型到类型 T装箱取消装箱转换。

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

有关 C# 转换的信息,请参阅 C# 语言规范转换一章。

有模式匹配的类型测试

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[] { 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

IEnumerable<int> numbers = new int[] { 10, 20, 30 };
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
    }
}

运算符可重载性

isastypeof 运算符无法进行重载。

用户定义的类型不能重载 () 运算符,但可以定义可由强制转换表达式执行的自定义类型转换。 有关详细信息,请参阅用户定义转换运算符

C# 语言规范

有关更多信息,请参阅 C# 语言规范的以下部分:

请参阅