对于这种C#类型转化我们通常用两种选择:使用as操作符,或者使用强制转型。当然还有一个做法是先用is测试转换是否可行,然后再用as或者强制转换。
本文主要给大家提醒一些使用C#类型转化时需要注意的地方,或许你更想弄清楚as和强制转换的区别和使用场景,不用着急,看完,本文你自己就知道答案了,那么上面提到的两种C#类型转化我们需要注意哪些呢?
1、as和is操作符都不执行任何用户自定义的转换。
2、对于强制转换,引用为空将会转换出错。
3、强制转换任意类型和自定义转换两种情况的IL代码展示有区别。
4、用户自定义转换只作用于对象的编译时类型。
5、as操作符不能应用于值类型。
6、foreach循环语句中使用强制类型转型。
下面我们逐一进行介绍说明:
1,我们先来看错误代码示例:
classA
{
}
classC
{
publicstaticimplicitoperatorA(Ct)
{
returnnewA();
}
}
classProgram
{
staticvoidMain(string[]args)
{
objecto=Factory.GetObject();
//o为一个C类型:
Aa=oasA;//转型失败,o的类型不是A
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
代码已经很明显,我们不可以因为定义了C到A的强制转换,就使用as,对应用户自定义转换,我们只可以使用(A)o转换,其实自定义转换和自定义操作=、+、-、"、%等操作符是一样的机理,这样你就明白为什么用as不可以使用自定义转换了。
2,还是看一个错误示例:
classA
{
}
classB:A
{
}
classProgram
{
staticvoidMain(string[]args)
{
Bb;
Aa=(A)b;
}
}
问题不用多言,as可以解决这个问题。
3,看一个示例:
classA
{
}
classC
{
publicstaticimplicitoperatorA(Ct)
{
returnnewA();
}
}
classB:A
{
}
classProgram
{
staticvoidMain(string[]args)
{
Aa=newB();
Bb=(B)a;
Cc=newC();
a=(A)c;
}
}
对于Bb=(B)a;的IL代码如下:
IL_0008:castclassConsoleApplication1.B
对于a=(A)c;的IL代码如下:
IL_0015:callclassConsoleApplication1.AConsoleApplication1.C::op_Implicit(classConsoleApplication1.C)
区别大家已经看到了,要说真正认识区别那么我们要继续谈“4、用户自定义转换只作用于对象的编译时类型。"
4,看一个示例:
classA
{
}
classC
{
publicstaticimplicitoperatorA(Ct)
{
returnnewA();
}
}
classB:A
{
}
classProgram
{
staticvoidMain(string[]args)
{
Aa=newB();
Bb=(B)a;
objectc=newC();
a=(A)c;//编译通过,运行失败!
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
也许你从来没想过a=(A)c;编译会成功,运行会出错在这句,毕竟我们有一个显示类型C到类型A的转换操作,我们看看IL代码即可找到答案,
.methodprivatehidebysigstaticvoidMain(string[]args)cilmanaged
{
.entrypoint
//Codesize28(0x1c)
.maxstack1
.localsinit([0]classConsoleApplication1.Aa,
[1]classConsoleApplication1.Bb,
[2]objectc)
IL_0000:nop
IL_0001:newobjinstancevoidConsoleApplication1.B::.ctor()
IL_0006:stloc.0
IL_0007:ldloc.0
IL_0008:castclassConsoleApplication1.B
IL_000d:stloc.1
IL_000e:newobjinstancevoidConsoleApplication1.C::.ctor()
IL_0013:stloc.2
IL_0014:ldloc.2
IL_0015:castclassConsoleApplication1.A
IL_001a:stloc.0
IL_001b:ret
}//endofmethodProgram::Main
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
大家注意看IL_0015:castclassConsoleApplication1.A这句,这句说明自定义转换在编译时刻进行,也许你要问为什么不是第3条的:
IL_0015:callclassConsoleApplication1.AConsoleApplication1.C::op_Implicit(classConsoleApplication1.C)
因为objectc,c被定义为Object类型,那么强制转换在编译时刻去Object找是否存在自定义转换操作(注意,()转型时编译器优先考虑自定义转换,找不到才进行castclass),当然Object没有自定义转换为A的操作,那么就使用普通的强制转换castclass。好了现在我们知道了用户自定义转换只作用于对象的编译时类型,而普通的Bb=(B)a;强制转换可以作用到运行时刻。那么上面的错误如何去掉呢?对应代码修改为:
objectc=newC();
Ccc=casC;
a=(A)cc;
- 1.
- 2.
- 3.
现在运行正常通过,好了我们再次查看IL代码
IL_001c:callclassConsoleApplication1.AConsoleApplication1.C::op_Implicit(classConsoleApplication1.C)
到这大家对3和4点的认识应该很清楚了吧。
5、as操作符不能应用于值类型————省略!,鉴于这个点很简单,本人就不提供示例了,大家有兴趣可以自己试验下。
6、看代码示例:(以下代码摘自《EffectiveC#中文版改善C#程序的50中方法》——23页)
publicvoidUseCollection(IEnumerabletheCollection)
{
foreach(MyTypetintheCollection)
t.DoStuff();
}
//上面代码等同于:
publicvoidUseCollection(IEnumerabletheCollection)
{
IEnumeratorit=hteCollection.GetEnumerator();
while(it.MoveNext())
{
MyTypet=(MyType)it.Current;
t.DoStuff();
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
通过查看IL代码我们可以确认foreach语句的转换是使用的强制转换操作,那么为什么呢?之所以使用强制转型,是因为foreach语句需要同时支持值类型和引用类型,这侧面说明我们的第5点as不支持值类型。
好了,5点C#类型转化说明已经解释完了,你现在还想问as和强制转换的区别和使用场景么?,欢迎提出批评、指正错误。
【编辑推荐】