首页 > 学院 > 开发设计 > 正文

C#中 As 和强制转换的总结

2019-11-06 06:22:20
字体:
来源:转载
供稿:网友

C#中 As 和强制转换的总结

1.1.1 摘要

C#是一门强类型语言,一般情况下,我们最好避免将一个类型强制转换为其他类型,但有些时候难免要进行类型转换。

先想想究竟哪些操作可以进行类型转换(先不考虑.NET提供的Parse),一般我们都有以下选择:

使用as操作符转换,使用传统C风格的强制转型使用is来做一个转换测试,然后再使用as操作符或者强制转

 

1.1.2 正文

正确的选择应该是尽可能地使用as操作符,因为它比强制转型要安全,而且在运行时层面也有比较好的效率(注意的是as和is操作符都不执行任何用户自定义的转换,只有当运行时类型与目标转换类型匹配时,它们才会转换成功)。

现在我们通过一个简单的例子说明as和强制转换之间的区别,首先我们定义一间获取不同类型对象的工厂,然后我们把未知类型转换为自定义类型。

 

复制代码
            object o = Factory.GetObject();                  MyType t = o as MyType;               if (t == null)            {                //转换成功                    }            else            {                //转换失敗                    }           object o = Factory.GetObject();                 try            {                MyType t = (MyType) o;                if (t != null)                {                    ////转换成功                 }                else                {                    ////转换失敗                 }             }            catch            {                ////异常处理            }复制代码

 

通过上述代码我们发现as类型转换失败时值为null不抛出异常,但强制转换如果转换失败会抛出异常所以我们要添加异常处理。

现在我们对as和强制转换有了初步的了解,假设现在我们定义了一个抽象类Foo,然后Foo1继承于它,并且再定义一个基类Logger,在Foo1中定义与Logger类型隐式转换具体如下:

复制代码
     Foo1 myFoo;          //// Inherits abstract class.     Logger myFoo;       //// base class.    public class Foo1 : Foo    {        PRivate Logger _value;        /// <summary>        /// 隐式自定义类型转换。                /// </summary>        /// <param name="foo1"></param>        /// <returns></returns>        public static implicit Operator Logger(Foo1 foo1)        {            return foo1._value;        }    }复制代码

现在我们猜猜看以下的类型转换是否成功(提示:从编译和运行时类型转换角度考虑)。

复制代码
                object myFoo = container.Resolve<Foo>();      //获取未Foo1类型                  try                {                    Logger myFoo1 = (Logger)myFoo;                    if (myFoo1 != null)                    {                        Console.WriteLine("Covert successful.");                    }                }                catch                {                    Console.WriteLine("Covert failed.");                }复制代码

相信聪明的大家已经想出答案了,激动人心的时刻到了现在让我们公布答案:转换失败抛出异常。

图1转换失败结果

 

首先我们要从编译和运行时角度来分析,在编译时myFoo的类型为System.Object,这时编译器会检测是否存在自定义由Object到Logger的类型转换。如果没有找到合适转换,编译器将生成代码检测myFoo的运行时类型和Logger比较,由于myFoo的运行时类型为Foo1,而且我们自定义了由Foo1到Logger的类型转换,估计这样可以转换成功了吧!然而恰恰没有转换成功,这究竟是什么原因呢?让我们了解一下编译器对于隐式类型转换的原理吧。

图2编译和运行时自定义类型转换

 

通过上图我们发现用户自定义的转换操作符只作用于对象的编译时类型,而非运行时类型上,OK现在让修改一下代码让我们编译器认识自定义类型中。

复制代码
            using (IUnityContainer container = new UnityContainer())            {                UnityConfigurationSection section = (UnityConfigurationSection)                    ConfigurationManager.GetSection("unity");   //获取container名称为CfgClass下的配置                section.Containers["CfgClass"].Configure(container);                object tempFoo = container.Resolve<Foo>();      //获取未Foo1类型                Foo1 myFoo = tempFoo as Foo1;          //使用as先把object转型为Foo1                try                {                    Logger myFoo1 = (Logger)myFoo;                    if (myFoo1 != null)                    {                        Console.WriteLine("Covert successful.");                    }                }                catch                {                    Console.WriteLine("Covert failed.");                }                Console.ReadKey();            }复制代码

图3转换成功结果

 

现在类型可以转换成功,这是因为编译器使用了我们自定义的隐式转换,由于myFoo这次的编译类型为Foo1,编译器首先查找是否存在Foo1和Logger自定义转换类型,由于我们定义了一种由Foo1到Logger的隐式类型转换所以转换成功。

通过上述我们发现了as给我们带来的好处,但是有一点我们要注意的是as只能用于引用类型不能用于值类型。那我就有个问题了在进行类型转换之前如果我们并不知道要转换的是值类型还是引用类型,那该怎么办呢?现在是is登场的时候了。

        bject tempFoo = container.Resolve<Foo>();      //获取未Foo1类型                int myInt = tempFoo as int;                 //compile error

as不能用于值类型,这是因为值类型不能为null(注意:C#2.0中,微软提供了Nullable类型,允许用它定义包含null值,即空值的数据类型)像这种情况我们应该使用强制类型转换。

复制代码
    object tempFoo = container.Resolve<Foo>();      //获取未Foo1类型                try                {                    int myInt = (int)tempFoo;                 //转换成功                          if (myFoo1 != null)                    {                        Console.WriteLine("Covert successful.");                    }                }                catch                {                    Console.WriteLine("Covert failed.");                }复制代码

大家可以发现和我们之前使用的强制转换类似,而且还有处理异常,现在修改一下我们代码让它更加简洁实现如下:

复制代码
object tempFoo = container.Resolve<Foo>();      //获取未Foo1类型int i = 0;                                      //值类型转换if (tempFoo is int){    i = (int) tempFoo;}object tempFoo = container.Resolve<Foo>();      //获取未Foo1类型Logger myFoo1 = null;                          //引用类型转换if (tempFoo is Logger){    myFoo1 = tempFoo as Logger;}复制代码

1.1.3 总结

as和强制转换之间最大的区别就在于如何处理用户自定义的转换。操作符 as和 is 都只检查被转换对象的运行时类型,并不执行其他的操作。如果被转换对象的运行时类型既不是所转换的目标类型,也不是其派生类型,那么转型将告失败。但是强制转型则会使用转换操作符来执行转型操作,这包括任何内建的数值转换(如:long转int)。

一般情况我们应该先考虑使用as进行类型转换,然后再考虑使用is,最后才考虑使用强制转换。

 

as

强制转换

转换失败是否抛出异常

No

Yes

支持值类型和引用类型转换

只支持引用类型

Yes

Jackson Huang

关于作者:

[作者]: JK_Rush从事.NET开发和热衷于开源高性能系统设计,通过博文交流和分享经验,欢迎转载,请保留原文地址,谢谢。[出处]:http://www.cnblogs.com/rush/[本文基于]:署名-非商业性使用 3.0许可协议发布,欢迎转载,演绎,但是必须保留本文的署名JK_Rush(包含链接),且不得用于商业目的。如您有任何疑问或者授权方面的协商,请与我联系。

分类: [02] C#好文要顶 关注我 收藏该文 JK_Rush关注 - 5粉丝 - 1418 荣誉:推荐博客+加关注 14 0 «上一篇:装饰者(Decorator)»下一篇:您不能不知的ToString()方法posted @ 2011-05-13 21:05 JK_Rush 阅读(17528) 评论(13)编辑 收藏评论列表  #1楼2011-05-13 21:20 Create Chen  as比is性能好根本原因说白了是它比is少执行类型检查
123456System.Object o =new System.Object(); if (o is SomeMyClass)  //第一次执行类型检查{SomeMyClass myClass = (SomeMyClass)o;  //第二次执行类型检查}
CLR执行了两次类型检查,因为这样,所以会降低性能.所以有了as操作符, 它可以避免执行两次类型检查. 如果兼容, as会返回一个指向同一个对象的指针, 不兼容的话就返回null, 上面的代码用as操作符写就如下:
1234567System.Object o =new System.Object(); SomeMyClass myClass = oas SomeMyClass;//只执行一次类型检查if (myClass != null){    //........}
和is一样,as也不会抛出异常支持(0)反对(0)  #2楼[楼主]2011-05-13 21:25 JK_Rush  @Create Chen是的,一般考虑到效率情况应该减少as-is一起使用。支持(0)反对(0)  #3楼2011-05-13 22:23 水牛刀刀  "编译时类型和运行时类型"这种说法似乎不妥,你似乎是想说“变量声明类型”和“对象的类型”。支持(0)反对(0)  #4楼2011-05-14 09:40 菜鸟进博客园眼花撩乱  
引用JK_Rush:@Create Chen是的,一般考虑到效率情况应该减少as-is一起使用。
VS对项目点右键 选择"代码分析"的时候as 和 is一起用就会显示成警告支持(0)反对(0)  #5楼2011-05-14 10:08 阿龍  很多时候,不在乎这点性能,也不乎它抛异常。该抛异常时就抛异常,不怕的。支持(0)反对(0)  #6楼[楼主]2011-05-14 10:25 JK_Rush  @菜鸟进博客园眼花撩乱您指的是VS在添加TFS之后的代码分析功能吗?支持(0)反对(0)  #7楼2011-05-14 17:10 菜鸟进博客园眼花撩乱  @JK_Rush我记得在公司的电脑上解决方案资源管理器里面 对着项目点右键 就有代码分析。我经常以分析就出几百个警告,包括命名不规范的警告,大小写不规范的警告。可是刚在自己电脑上试了,却没这个选项。不知道是不是VS版本的区别,或者这是一个插件功能支持(0)反对(0)  #8楼[楼主]2011-05-14 18:53 JK_Rush  @菜鸟进博客园眼花撩乱你公司电脑安装了Team Foundation Server吧!支持(0)反对(0)  #9楼2011-05-15 08:56 菜鸟进博客园眼花撩乱  @JK_Rush没有装TFS记得好像是微软的2款代码检测插件。非常严格,连私有字段 属性 私有方法 公有方法 的顺序都不能错支持(0)反对(0)  #10楼[楼主]2011-05-15 10:56 JK_Rush  @菜鸟进博客园眼花撩乱有一个微软的代码插件的确比较严格:StyleCop,用它分析代码会出现很多警告。支持(0)反对(0)  #11楼2015-03-12 14:52 conanVista  https://msdn.microsoft.com/zh-cn/library/ms182271.aspx如果在执行实际的强制转换之前,使用 C# is 运算符来测试强制转换是否将成功,请考虑转为测试 as 运算符的结果。 后者可以提供相同的功能,但不需要由 is 运算符执行的隐式强制转换操作。看起来 is 实现是 try { ? = (T)obj } catch { }as 比 is 好。支持(0)反对(0)  #12楼2015-03-27 11:45 njl_041x  研究的有深度,果断关注大牛!越是看到像你们这样的大牛,越觉得自己学的肤浅~支持(0)反对(0)  #13楼2015-07-13 10:11 佯林  直接在快速启动里面输入“代码分析”既可启动那个窗口
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表