一、is与as运算符和typeof运算符
1.1、is运算符
is
运算符检查一个对象是否兼容于其他指定的类型,并返回一个Bool值:
①如果一个对象是某个类型或是其父类型的话就返回为true,否则的话就会返回为false。永远不会抛出异常。
②如果对象引用为null,那么is操作符总是返回为false,因为没有对象可以检查其类型。
1.2、as运算符
as
运算符将表达式结果显式转换为给定的引用或可以为 null 值的类型。 如果无法进行转换,则 as
运算符返回 null
。 与强制转换表达式 不同, 运算符永远不会引发异常。
1.3、typeof运算符
typeof
运算符用于获取某个类型的 System.Type 实例。 typeof
运算符的实参必须是类型或类型形参的名称。
注意:
C#是一门强类型语言,一般情况下,我们最好避免将一个类型强制转换为其他类型,但有些时候难免要进行类型转换。
其中C#关于类型转换一般我们有以下选择:
①使用as操作符转换;
②使用传统C风格的强制转型;
③使用is来做一个转换测试,然后再使用as操作符或者强制转;
在编程过程中我们应该尽可能地使用as操作符,因为它比强制转型要安全,而且在运行时层面也有比较好的效率(注意的是as和is操作符都不执行任何用户自定义的转换,只有当运行时类型与目标转换类型匹配时,它们才会转换成功).
二、is运算符
2.1、is运算符的形式
is
运算符的形式如下:
E is T
其中 E
是返回一个值的表达式,T
是类型或类型参数的名称。 E
不得为匿名方法或 Lambda 表达式。
如果表达式结果为非 null 并且满足以下任一条件,则 is
运算符将返回 true
:
-
表达式结果的运行时类型为
T
。 -
表达式结果的运行时类型派生自类型
T
、实现接口T
,或者存在从其到T
的另一种T
。 -
表达式结果的运行时类型是基础类型为 且 Nullable<T>.HasValue 为
true
的可为空值类型。
is
运算符不会考虑用户定义的转换。
2.2、is运算符典型示例
《1》如果表达式结果的运行时类型派生自给定类型,即类型之间存在引用转换,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
}
}
《2》is
运算符将考虑装箱和取消装箱转换,但不会考虑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
《3》 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运算符
3.1、as运算符的形式
as运算符的形式如下:
E as T
其中,E
为返回值的表达式,T
为类型或类型参数的名称,生成相同的结果。
《1》as
运算符仅考虑引用、可以为 null、装箱和取消装箱转换。 不能使用 as
运算符执行用户定义的转换。 为此,请使用强制转换表达式。
IEnumerable<int> numbers = new[] { 10, 20, 30 };
IList<int> indexable = numbers as IList<int>;
if (indexable != null)
{
// output: 40
Console.WriteLine(indexable[0] + indexable[indexable.Count - 1]);
}
3.2、强制转换表达式
形式为 (T)E
的强制转换表达式将表达式 E
的结果显式转换为类型 T
。 如果不存在从类型 E
到类型 T
的显式转换,则发生编译时错误(需要配合try catch finally来处理,效率较低)。 在运行时,显式转换可能不会成功,强制转换表达式可能会引发异常。
下面的示例演示显式数值和引用转换:
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
四、typeof运算符
4.1、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 方法。
4.2、typeof示例
使用 typeof
运算符来检查表达式结果的运行时类型是否与给定的类型完全匹配。 以下示例演示了使用 typeof
运算符和 typeof
执行的类型检查之间的差异:
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运算符使用场景
序号 | is与as运算符的使用场景 |
1 | Object => 已知引用类型——使用as操作符完成 |
2 | Object => 已知值类型——先使用is操作符来进行判断,再用类型强转换方式进行转换; |
3 | 已知引用类型之间转换——首先需要相应类型提供转换函数,再用类型强转换方式进行转换 |
4 | 已知值类型之间转换——最好使用系统提供的Convert类所涉及的静态方法 |