说说C#的类型转换

  C#是 一门强类型的语言。大家都写过很多以System.Object类型为参数的函数,在这些函数内部,我们经常要吧那些参数向下转型为其他类型。对于 这种转型我们通常用两种选择:使用as操作符,或者使用强制转型。当然还有一个做法是先用is测试转换是否可行,然后再用as或者强制转换。

   本文主要给大家提醒一些使用转型时需要注意的地方, 或许你更想弄清楚as和强制转换的区别和使用场景,不用着急,看完,本文你自己就知道答案了,那么上面提到的两种转型我们需要注意哪些呢?

     1、as和is操作符都不执行任何用户自定义的转换。

     2、对于强制转换,引用为空将会转换出错。

     3、强制转换任意类型和自定义转换两种情况的IL代码展示有区别。 

     4、用户自定义转换只作用于对象的编译时类型。

     5、as操作符不能应用于值类型。

     6、foreach循环语句中使用强制类型转型。

   下面我们逐一进行介绍说明:

     1, 我们先来看错误代码示例:

class A
{

}
class C
{
  
public static implicit operator A(C t)
  {
  
return new A();
  }
}
class Program
{
  
static void Main(string[] args)
 {
   
object o = Factory.GetObject();
   
//o为一个C类型:
   A a = o as A;//转型失败,o的类型不是A
  }
}

 

     代码已经很明显,我们不可以因为定义了C到A的强制转换,就使用as,对应用户自定义转换,我们只可以使用(A)o转换,其实自定义转换和自定义操作=、+、-、"、%等操作符是一样的机理,这样你就明白为什么用as不可以使用自定义转换了。

     2,还是看一个错误示例:

class A
    {
 
    }
    
class B : A
    {

    }
    
class Program
    {
        
static void Main(string[] args)
        {
           B b;
           A a
=(A)b;
         }
     }

   问题不用多言,as可以解决这个问题。

     3,看一个示例:

 class A
    {
 
    }
    
class C
    {
        
public static implicit operator A(C t)
        {
            
return new A();
        }
    }
    
class B : A
    {

    }
    
class Program
    {
        
static void Main(string[] args)
        {
            A a 
= new B();   
            B b 
= (B)a;
            C c 
= new C();
            a 
= (A)c;
         }
      }

    对于 B b = (B)a;的IL代码如下:

 IL_0008:  castclass  ConsoleApplication1.B

    对于a = (A)c;的IL代码如下:

 IL_0015:  call       class ConsoleApplication1.A ConsoleApplication1.C::op_Implicit(class ConsoleApplication1.C)

  区别大家已经看到了,要说真正认识区别那么我们要继续谈“4、用户自定义转换只作用于对象的编译时类型。"

     4,看一个示例:

class A
    {
 
    }
    
class C
    {
        
public static implicit operator A(C t)
        {
            
return new A();
        }
    }
    
class B : A
    {

    }
    
class Program
    {
        
static void Main(string[] args)
        {
            A a 
= new B();   
            B b 
= (B)a;
            
object c = new C();
            a 
= (A)c;//编译通过,运行失败!
          }
      }

  也许你从来没想过= (A)c;编译会成功,运行会出错在这句,毕竟我们有一个显示类型C到类型A的转换操作,我们看看IL代码即可找到答案,

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  
// Code size       28 (0x1c)
  .maxstack  1
  .locals init ([
0class ConsoleApplication1.A a,
           [
1class ConsoleApplication1.B b,
           [
2object c)
  IL_0000:  nop
  IL_0001:  newobj     instance 
void ConsoleApplication1.B::.ctor()
  IL_0006:  stloc.
0
  IL_0007:  ldloc.
0
  IL_0008:  castclass  ConsoleApplication1.B
  IL_000d:  stloc.
1
  IL_000e:  newobj     instance 
void ConsoleApplication1.C::.ctor()
  IL_0013:  stloc.
2
  IL_0014:  ldloc.
2
  IL_0015:  castclass  ConsoleApplication1.A
  IL_001a:  stloc.
0
  IL_001b:  ret
// end of method Program::Main

 

  大家注意看  IL_0015:  castclass  ConsoleApplication1.A这句,这句说明自定义转换在编译时刻进行,也许你要问为什么不是第3条的:

 IL_0015:  call       class ConsoleApplication1.A ConsoleApplication1.C::op_Implicit(class ConsoleApplication1.C)

  因为object c,c被定义为Object类型,那么强制转换在编译时刻去Object找是否存在自定义转换操作(注意,()转型时编译器优先考虑自定义转换,找不到才进行castclass),当然Object没有自定义转换为A的操作,那么就使用普通的强制转换castclass。好了现在我们知道了用户自定义转换只作用于对象的编译时类型,而普通的 B b = (B)a;强制转换可以作用到运行时刻。那么上面的错误如何去掉呢?对应代码修改为:

   object c = new C();
     C cc 
= c as C;
     a 
= (A)cc;

 现在运行正常通过,好了我们再次查看IL代码

 IL_001c:  call       class ConsoleApplication1.A ConsoleApplication1.C::op_Implicit(class ConsoleApplication1.C)

  到这大家对3和4点的认识应该很清楚了吧。

     5、as操作符不能应用于值类型————省略!,鉴于这个点很简单,本人就不提供示例了,大家有兴趣可以自己试验下。

     6、看代码示例:(以下代码摘自《Effective C#中文版 改善C#程序的50中方法》——23页)

public void UseCollection (IEnumerable theCollection)
{
 
foreach ( MyType t in theCollection)
 t.DoStuff();
}

//上面代码等同于:

public void UseCollection (IEnumerable theCollection)
{
 IEnumerator it 
= hteCollection.GetEnumerator();
 
while (it.MoveNext())
 {
   MyType t 
= (MyType)it.Current; 
   t.DoStuff();
 }
}

   通过查看IL代码我们可以确认foreach语句的转换是使用的强制转换操作,那么为什么呢?之所以使用强制转型,是因为foreach语句需要同时支持值类型和引用类型,这侧面说明我们的第5点as不支持值类型。

  好了,5点说明已经解释完了,你现在还想问as和强制转换的区别和使用场景么?,欢迎提出批评、指正错误。

另外祝大家情人节快乐!当然没有情人的不用,更不要和哥们

 

LoveBaoBao

作者:LoveBaoBao

出处:http://lovebaobao.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

      
posted @ 2009-02-13 17:10  张蒙蒙  阅读(3464)  评论(5编辑  收藏