建议3: 区别对待强制转型与as和is

转载 2016年08月29日 11:45:56

建议3: 区别对待强制转型与as和is

在阐述本建议之前,首先需要明确什么是强制转型,以及强制转型意味着什么。从语法结构上来看,类似下面的代码就是强制转型。

secondType = (SecondType)firstType; 

但是,强制转型可能意味着两件不同的事情:

1)FirstType和SecondType彼此依靠转换操作符来完成两个类型之间的转型。

2)FirstType是SecondType的基类。

类型之间如果存在强制转型,那么它们之间的关系,要么是第一种,要么是第二种,不能同时既是继承的关系,又提供了转型符。

首先看第一种情况,当FirstType和SecondType存在转换操作符时的代码如下:

复制代码
    class FirstType  
    {  
        public string Name { get; set; }  
    }  
     
    class SecondType  
    {  
        public string Name { get; set; }  
        public static explicit operator SecondType(FirstType firstType)  
        {  
            SecondType secondType = new SecondType() { Name = "转型自:" + firstType.Name };  
            return secondType;  
        }  
    } 
复制代码

 

在这种情况下,如果想转型成功则必须使用强制转型,而不是使用as操作符。

    FirstType firstType = new FirstType() { Name = "First Type" };  
    SecondType secondType = (SecondType)firstType;         //转型成功  
    //secondType = firstType as SecondType;     //编译期转型失败,编译通不过 

 

不过,这里需要讨论的不是像以上代码这样的简单应用,而是稍微复杂一点的应用。为了满足更进一步的需求,我们需要写一个通用的方法,需要对FirstType或者SecondType做一些处理,方法看起来应该像下面这样:

    static void DoWithSomeType(object obj)  
    {  
        SecondType secondType = (SecondType)obj;  
    } 

 

注意 是否对这种方法声明方式有一点熟悉?事实上,如果再加一个参数EventArgs,上面的方法就可以注册成为一个典型的CLR事件方法了。

如果运行本段代码,会带来一个问题:若在调用方法的时候,传入的参数是一个FirstType对象,那就会引发异常。你可能会问,在上一段代码中,有这样的写法:

    FirstType firstType = new FirstType() { Name = "First Type" };  
    SecondType secondType = (SecondType)firstType; 

 

而DoWithSomeType方法提供的代码,看起来无非像下面这样:

    FirstType firstType = new FirstType() { Name = "First Type" };  
    object obj = firstType;  
    SecondType secondType = (SecondType) obj; 

 

也就是说,这段代码与上段代码相比,仅仅多了一层转型,实际上obj还是firstType,为什么转型就失败了呢?这是因为编译器还不够聪明,或 者说我们欺骗了编译器。针对(SecondType) obj,编译器首先判断的是:SecondType和object之间有没有继承关系。因为在C#中,所有的类型都是继承自object的,所以上面的代 码编译起来肯定没有问题。但是编译器会自动产生代码来检查obj在运行时是不是SecondType,这样就绕过了转换操作符,所以会转换失败。因此,这 里的建议是:

如果类型之间都上溯到了某个共同的基类,那么根据此基类进行的转型(即基类转型为子类本身)应该使用as。子类与子类之间的转型,则应该提供转换操作符,以便进行强制转型。

注意 再次强调,转型操作符实际上就是一个方法,类型的转换需要手工写代码完成。

为了编写更健壮的DoWithSomeType方法,应该按如下方式改造它:

复制代码
    static void DoWithSomeType(object obj)  
    {  
        SecondType secondType = obj as SecondType;  
        if (secondType != null)  
        {  
            //  省略  
        }  
    } 
复制代码

 

as操作符永远不会抛出异常,如果类型不匹配(被转换对象的运行时类型既不是所转换的目标类型,也不是其派生类型),或者转型的源对象为null, 那么转型之后的值也为null。改造前的DoWithSomeType方法会因为引发异常带来效率问题,而使用as后,就可以完美地避免这种问题。

现在,再来看第二种情况,即FirstType是SecondType的基类。在这种情况下,既可以使用强制转型,也可以使用as操作符,代码如下所示:

复制代码
    class Program  
    {  
        static void Main(string[] args)  
        {  
            SecondType secondType = new SecondType() { Name = "Second Type" };  
            FirstType firstType1 = (FirstType)secondType;  
            FirstType firstType2 = secondType as FirstType;  
        }  
    }  
     
    class FirstType  
    {  
        public string Name { get; set; }  
    }  
     
    class SecondType : FirstType  
    {  
    } 
复制代码

 

但是,即使可以使用强制转型,从效率的角度来看,也建议大家使用as操作符。

知道了强制转型和as之间的区别,我们再来看一下is操作符。DoWithSomeType的另一个版本,可以这样来实现,代码如下所示:

复制代码
    static void DoWithSomeType(object obj)  
    {  
        if (obj is SecondType)  
        {  
            SecondType secondType = obj as SecondType;  
            //省略  
        }  
    } 
复制代码

 

这个版本显然没有上一个版本的效率高,因为当前这个版本进行了两次类型检测。但是,as操作符有一个问题,即它不能操作基元类型。如果涉及基元类型的算法,就需要通过is转型前的类型来进行判断,以避免转型失败。

 

转自:《编写高质量代码改善C#程序的157个建议》陆敏技

Swift入门(十一)——类型转换与is、as操作

三种操作:is、as?和as!Swift是强类型语言,但也允许开发者通过is、as?和as!这三种操作来对类型进行判断和强制转换。其中is用作类型判断,而as?和as!则分别是类型转换的可选形式和强制...
  • abc649395594
  • abc649395594
  • 2015年08月29日 00:45
  • 2424

C#的as, is和类型强制转换

C#的as和强制转换
  • DianaCody
  • DianaCody
  • 2014年04月21日 08:14
  • 1278

static_cast与c风格的强制类型转换比较

class A { int a; }; class B { int b; }; class C : public A { int c; }; int main() { ...
  • whatday
  • whatday
  • 2015年12月28日 10:51
  • 3420

黑马程序员--.Net学习日记——C#强制转换as is 的用法和区别

黑马程序员--.Net学习日记——C#强制转换as is的用法和区别 ----------------------Windows Phone 7手机开发、Net培训、期待与您交流! ----...
  • gao490
  • gao490
  • 2012年07月25日 16:26
  • 206

建议94:区别对待override和new

建议94:区别对待override和new override和new使类型体系应为继承而呈现出多态性。多态要求子类具有与基类同名的方法,override和new的作用就是: 如果子类中的方法前面带...
  • houwc
  • houwc
  • 2016年09月08日 15:46
  • 206

[C#] as 和 is 运算符以及安全的类型强制转换

根据MSDN的说明:由于对象是多态的,因此基类类型的变量可以保存派生类型。若要访问派生类型的方法,需要将值强制转换回该派生类型。不过,在这些情况下,如果只尝试进行简单的强制转换,会导致引发 Inval...
  • open2open2
  • open2open2
  • 2014年02月28日 10:45
  • 619

改善C++ 程序的150个建议学习之建议24:尽量采用C++风格的强制转型

建议24:尽量采用C++风格的强制转型 在建议11中,我们详细讲述了强制转型存在的一些问题,并建议在代码编写过程中尽量避免使用这个招人讨厌的东西。然而,正如哲学中所讲的一样:存在的即是合理的。强制转型...
  • baliguan163
  • baliguan163
  • 2013年09月12日 08:56
  • 645

改善C++ 程序的150个建议学习之建议11:将强制转型减到最少

建议11:将强制转型减到最少 C++ 在设计中一直强调类型安全,而且也采取了一定的措施来保障这条准则的执行。但是,从C继承而来的强制转型却破坏了C++类型系统,C中的强制转型可谓是“无所不能”,其超强...
  • baliguan163
  • baliguan163
  • 2013年09月11日 09:08
  • 683

C#中is和as、向上强制转换和向下强制装换之详解

首先,第一个出现的是is,有时,我们需要查看某个类是否实现了一个接口,这个时候就可以使用is关键字来找出答案: for(int I = 0; i< bees.Length; i++){ ...
  • u013180863
  • u013180863
  • 2015年09月05日 11:02
  • 650

提高C#编程水平的50个要点 1.总是用属性 (Property) 来代替可访问的数据成员 2.在 readonly 和 const 之间,优先使用 readonly 3.在 as 和 强制类型转换之

提高C#编程水平的50个要点 1.总是用属性 (Property) 来代替可访问的数据成员 2.在 readonly 和 const 之间,优先使用 readonly 3.在 as ...
  • u014515784
  • u014515784
  • 2014年04月15日 17:53
  • 513
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:建议3: 区别对待强制转型与as和is
举报原因:
原因补充:

(最多只允许输入30个字)