Effective C#: Item 3: Prefer the is or as Operators to Casts
Item 3: Prefer the is or as Operators to Casts
C#是强类型语言.我们要尽量避免类型转换.
有时我们必须要在runtime检查一个变量的类型.比如有时你要用到一些.Net framework提供的方法,这些方法需要用到System.Object类型的参数.你需要把这些object (方法的参数)向下cast成其他的类型(类或者interface),这时你有两个基本方式可以选择,一是使用as操作符,二是使用C语言风格的cast.两者也可以结合成一个更加保险的方法,就是先用is 操作符来测试类型的转换,然后再用cast或者as 操作符进行转换.
正确的选择应该是使用as操作符来进行类型转换. as操作符比碰运气型的cast更加的安全,而且在runtime更加的高效. as和is操作符并不能进行所有的用户定义的类型转换, 只有当runtime类型和目标类型一致时转换操作才会成功.它们永远不会为了满足程序调用请求而创建一个新的object.
在下例中,你需要把一个object转换成一个MyType的实例,你可以这样实现:
object o = Factory.GetObject( );<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
// Version one:
MyType t = o as MyType;
if ( t != null )
{
// work with t, it's a MyType.
}
else
{
// report the failure.
}
也可以这样写:
object o = Factory.GetObject( );
// Version two:
try
{
MyType t;
t = ( MyType ) o;
if ( t != null )
{
// work with T, it's a MyType.
}
else
{
// Report a null reference failure.
}
}
catch
{
// report the conversion failure.
}
第一个方法明显更加的简单易读,而且也没有try/catch的overhead.代码更加的高效.我们注意到cast版本不仅要检查转换后的object是否为null,还要catch异常.但as操作符版本却不用. 这是因为使用cast时, null可以被转换成任何一种reference类型,但是当应用as操作符在一个null reference上时会返回null. 所以as操作符只需检查一下返回的reference是否为null, 而不用catch异常.
as操作符和cast操作符最大的不同是如何对待用户定义的转换. as和is操作符只会检查被转换的object的runtime类型,而不做任何其他的工作. 如果这个object不是目标类型,或者不是目标类型的子类型, 操作失败并终止. 但cast操作符却不同,它会把object转换成目标类型, 这包括所有的numeric转换, 比如从long转换成short, object的一些信息就在这种转换中丢失了.
而且当cast用户自定义类型时也会有同样的问题.比如:
public class SecondType
{
private MyType _value;
// other details elided
// Conversion operator.
// This converts a SecondType to
// a MyType, see item 29.
public static implicit operator
MyType( SecondType t )
{
return t._value;
}
}
假设我们用Factory.GetObject()生成了一个SecondType的object,并把它转换成MyType类型.
object o = Factory.GetObject( );
// o is a SecondType:
MyType t = o as MyType; // Fails. o is not MyType
if ( t != null )
{
// work with t, it's a MyType.
}
else
{
// report the failure.
}
// Version two:
try
{
MyType t1;
t = ( MyType ) o; // Fails. o is not MyType
if ( t1 != null )
{
// work with t1, it's a MyType.
}
else
{
// Report a null reference failure.
}
}
catch
{
// report the conversion failure.
}
两个版本都会失败.但是cast却执行了用户定义的转换.这种假象使你认为cast成功了.但实际上它是失败的,因为编译器会根据编译时object的类型来生成代码.编译器对运行时object的类型一无所知.它只是把o当作System.Object的一个实例.编译器没有发现从System.Object到MyType可行的转换.它检查System.Object和MyType的定义,因为缺少用户定义的转换信息,编译器生成代码来检查o的运行时的类型,然后检查它是不是MyType类型.因为o是SecondType类型,所以转换失败.编译器并不检查o在运行时的类型是否可以转换成MyType类型.
如果你想让转换成功,可以这样写代码:
object o = Factory.GetObject( );
// Version three:
SecondType st = o as SecondType;
try
{
MyType t;
t = ( MyType ) st;
if ( t != null )
{
// work with T, it's a MyType.
}
else
{
// Report a null reference failure.
}
}
catch
{
// report the failure.
}
你永远也不应该写这样丑陋的代码,但这也是一个常见的问题.尽管你永远不应该写这样的代码,但你可以用System.Object来当作一个进行转换操作的function的参数,比如:
object o = Factory.GetObject( );
DoStuffWithObject( o );
private void DoStuffWithObject( object o2 )
{
try
{
MyType t;
t = ( MyType ) o2; // Fails. o is not MyType
if ( t != null )
{
// work with T, it's a MyType.
}
else
{
// Report a null reference failure.
}
}
catch
{
// report the conversion failure.
}
}
用户自定义的转换只作用于编译时object的类型,而不时运行时的类型. 至于运行时是否存在o2和MyType类型的转换, 编译器不知道也根本不关心. 但当st类型不同时,这个语句有着不同的表现:
t = ( MyType ) st;
上面的语句会调用用户自定义的转换,从而造成转换成功的假象. 但使用as操作符的语句却有着一致的表现.所以应该尽量的使用as操作符.如下面的语句:
t = st as MyType;
事实上,如果st和MyType之间不存在继承关系的话,而是通过一个用户自定义的转换来进行类型转换,那么编译器会报告一个错误.
现在你知道了应该尽可能的使用as操作符.但也有一些情况不能使用它.as操作符不能作用于value type上.下面的这个语句不会通过编译:
object o = Factory.GetValue( );
int i = o as int; // Does not compile.
因为int是值类型,永远不能为null.那么如果o不是整数类型的话, i里面应该存什么值呢? 所以你不能使用as操作符.你可以用下面这种变通的方式:
object o = Factory.GetValue( );
int i = 0;
try
{
i = ( int ) o;
}
catch
{
i = 0;
}
但你不必一定这样一来做,不要忘了is操作符,你可以在转换之前先判断o的类型:
object o = Factory.GetValue( );
int i = 0;
if ( o is int )
i = ( int ) o;
如果o不是整数类型,那么is操作符返回false. is操作符作用于null arguments上时,永远返回false;
但你应该只在你不能使用as操作符转换类型时使用is,否则就是重复的, 比如:
// correct, but redundant:
object o = Factory.GetObject( );
MyType t = null;
if ( o is MyType )
t = o as MyType;
上面的代码和下面的代码是等效的:
// correct, but redundant:
object o = Factory.GetObject( );
MyType t = null;
if ( ( o as MyType ) != null )
t = o as MyType;
可以看出,进行了两次转换,低效而且重复.如果你已经决定了要使用as操作符来转换类型,那么只需检查返回值是否为null就可以了.
现在你已经明白了as, is和cast,那么foreach循环用的是什么操作符呢?
public void UseCollection( IEnumerable theCollection )
{
foreach ( MyType t in theCollection )
t.DoStuff( );
}
foreach用的实际上是cast操作符.上面的代码可以重写成下面的代码:
public void UseCollection( IEnumerable theCollection )
{
IEnumerator it = theCollection.GetEnumerator( );
while ( it.MoveNext( ) )
{
MyType t = ( MyType ) it.Current;
t.DoStuff( );
}
}
这是因为foreach要用cast来支持value type和reference type. 如果使用as操作符的话,foreach语句仍表现相同的行为,但会抛出BadCastException,因为as不能作用于值类型上.
因为IEnumerator.Current返回一个System.Object类型的object,而这个object不具备转换操作,所以并不能用于这个测试. SecondType类型的collection也不能用于UseCollection()因为转换会失败. Foreach语句并不检查collection中object的运行时类型是否支持这种转换,它只检查IEnumerator.Current所返回的System.Object类型是否支持到目标类型(本例中的MyType)之间的转换.
最后,有时你想知道一个object确切的类型,而不只是关心这个object是否可以转换成目标类型. 因为as操作符对于所有从目标类型继承而来的类型的转换都返回true. 但GetType()方法返回object运行时的类型,它比as或者is操作符提供的测试都要更严格. 它返回的是object的确切类型.
再看一下UseCollection():
public void UseCollection( IEnumerable theCollection )
{
foreach ( MyType t in theCollection )
t.DoStuff( );
}
如果你创建一个叫NewType的类,这个类继承MyType,那么NewType objects的collection也可以在UseCollection()中很好的工作.
public class NewType : MyType
{
// contents elided.
}
如果你的意图是写出一个可以使用所有MyType类型(自身或继承而来)的function时,这没有什么.但如果你的意图是写一个只接受MyType类型自身的function的话,你就要用确切的类型来进行比较. 在本例中,你可以在foreach循环里做. 知道运行时确切的类型只有在做equality测试时是非常重要的.在大多数其他的情况下,as和is操作符提供的isinst比较是语法上正确的.
好的OO经验告诉我们要尽量避免类型的转换,但有时类型转换是必需的.在这种情况下,尽量的使用as和is操作符来表达你的意图. 不同的类型强制转换有不同的规则,但as和is却在绝大多数情况下都是正确的,而且它们只有在object是正确的类型时才转换成功. Cast操作符会带来一些副作用,而且转换的成功与失败往往出乎意料.
本系列文章只是作者读书笔记,版权完全属于原作者 (Bill Wagner),任何人及组织不得以任何理由以商业用途使用本文,任何对本文的引用和转载必须通知作者:zphillm@hotmail.com
转载于:https://www.cnblogs.com/ZphillM/archive/2005/08/06/208713.html
Effective C#: Item 3: Prefer the is or as Operators to Casts相关推荐
- Item 13: Prefer const_iterators to iterators.
Item 13: Prefer const_iterators to iterators. Effective Modern C++ Item 13 的学习和解读. STL 中 const_itera ...
- Effective C#: Item 1 Always use properties instead of accessible data members
Effective C#: Item 1 Always use properties instead of accessible data members Item 1: 当设计类时,永远用Prope ...
- Item 02: Prefer consts, enums, and inlines to #defines
Item 02: Prefer consts, enums, and inlines to #defines 尽量以 const,enum,inline 替换 #define 假如有这样的语句: #d ...
- Item 20: Prefer pass-by-reference-to-const to pass-by-value(Effective C++)
Prefer pass-by-reference-to-const over pass-by-value. It's typically more efficient and it avoids th ...
- 《Effective Morden C++》Item 8: Prefer nullptr to 0 and NULL.
引子 这一条目就比较简单了,就是宣传用nullptr来指代空指针,而不是之前的0或者NULL. 正文 在老式C++中,显然0是int类型,而NULL也是一个整数类型(int或者long).总的来说,这 ...
- Effective JavaScript Item 37 认识this的隐式指向
本系列作为Effective JavaScript的读书笔记. CSV数据通常都会被某种分隔符进行分隔.所以在实现CSV Reader时,须要支持不同的分隔符.那么,非常自然的一种实现就是将分隔符作为 ...
- Effective JavaScript Item 23 永远不要修改arguments对象
本系列作为Effective JavaScript的读书笔记. arguments对象只是一个类似数组的对象,但是它并没有数组对象提供的方法,比如shift,push等.因此调用诸如:argument ...
- More Effective C++ Item 附2:一个auto_ptr的实现实例
More Effective C++的前言.导读和附1(侯捷译),以及在 "C++ 中计算物件个数"和"为智能指标实作 operator->*"(陈崴译, ...
- Effective Java - Item 1: Consider static factory methods instead of constructors
考虑使用静态工厂方法来替代构造方法, 这样的做的好处有四点. 1. 更好的表意 有的构造方法实际上有特殊的含义, 使用静态工厂方法能更好的表达出他的意思. 例如 BigInteger(int, int ...
最新文章
- 推荐一个不错的开源在线HTML编辑器
- putty和Xming server 结合完美在windows下显示linux GUI程序
- spring源码分析之context
- 计算机控制实验教程,新)《计算机控制技术》实验教程.doc
- 探究Jvm源码实现-MarkWord
- Git的多人协作和分支处理测试
- HDFS查看文件的前几行-后几行-行数
- Linux 环境下 vi/vim 编辑器常用命令
- 交通信息工程 实验四:交通仿真实验(二)
- android仿ios录音动画,仿IOS录音机
- Java8(JDK1.8)新特性
- Unity任意版本Vuforia插件下载
- 联想主板怎么进入bios
- 谈一谈url实现文件下载
- iOS好用的第三方框架/插件
- ORACLE内核参数
- Golang的Ticker使用姿势
- Python--变量
- sql --Acess
- 数据库系统概念-第六版 - charter 1 - 笔记
热门文章
- 两种删除internal table entry的性能比较
- Java Spring实现原理研究之Servlet initialization初始化过程
- 为什么Kubernetes要引入pod的概念,而不直接操作Docker容器
- Linux系统里让vim支持markdown格式的语法高亮
- MongoDB最简单的入门教程之五-通过Restful API访问MongoDB
- 如何将iso文件安装到VirtualBox里的ubuntu去
- 计算机组成原理中wr是什么,计算机组成原理复习例题.doc
- python 量化交易 框架 开源_Hikyuu首页、文档和下载 - 基于 C++/Python 的开源量化交易研究框架 - OSCHINA - 中文开源技术交流社区...
- tstringlist怎么查看是否存在该数据_财务报表审计该如何进行?
- dll已加载但找不到入口点dllregisterserver_Java 是如何加载类的?