编写高质量代码改善C#程序的157个建议——建议3: 区别对待强制转型与as和is
建议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个建议》陆敏技
编写高质量代码改善C#程序的157个建议——建议3: 区别对待强制转型与as和is的更多相关文章
- 编写高质量代码改善C#程序的157个建议[1-3]
原文:编写高质量代码改善C#程序的157个建议[1-3] 前言 本文主要来学习记录前三个建议. 建议1.正确操作字符串 建议2.使用默认转型方法 建议3.区别对待强制转换与as和is 其中有很多需要理 ...
- 读书--编写高质量代码 改善C#程序的157个建议
最近读了陆敏技写的一本书<<编写高质量代码 改善C#程序的157个建议>>书写的很好.我还看了他的博客http://www.cnblogs.com/luminji . 前面部 ...
- 编写高质量代码改善C#程序的157个建议——建议157:从写第一个界面开始,就进行自动化测试
建议157:从写第一个界面开始,就进行自动化测试 如果说单元测试是白盒测试,那么自动化测试就是黑盒测试.黑盒测试要求捕捉界面上的控件句柄,并对其进行编码,以达到模拟人工操作的目的.具体的自动化测试请学 ...
- 编写高质量代码改善C#程序的157个建议——建议156:利用特性为应用程序提供多个版本
建议156:利用特性为应用程序提供多个版本 基于如下理由,需要为应用程序提供多个版本: 应用程序有体验版和完整功能版. 应用程序在迭代过程中需要屏蔽一些不成熟的功能. 假设我们的应用程序共有两类功能: ...
- 编写高质量代码改善C#程序的157个建议——建议155:随生产代码一起提交单元测试代码
建议155:随生产代码一起提交单元测试代码 首先提出一个问题:我们害怕修改代码吗?是否曾经无数次面对乱糟糟的代码,下决心进行重构,然后在一个月后的某个周一,却收到来自测试版的报告:新的版本,没有之前的 ...
- 编写高质量代码改善C#程序的157个建议——建议154:不要过度设计,在敏捷中体会重构的乐趣
建议154:不要过度设计,在敏捷中体会重构的乐趣 有时候,我们不得不随时更改软件的设计: 如果项目是针对某个大型机构的,不同级别的软件使用者,会提出不同的需求,或者随着关键岗位人员的更替,需求也会随个 ...
- 编写高质量代码改善C#程序的157个建议——建议153:若抛出异常,则必须要注释
建议153:若抛出异常,则必须要注释 有一种必须加注释的场景,即使异常.如果API抛出异常,则必须给出注释.调用者必须通过注释才能知道如何处理那些专有的异常.通常,即便良好的命名也不可能告诉我们方法会 ...
- 编写高质量代码改善C#程序的157个建议——建议152:最少,甚至是不要注释
建议152:最少,甚至是不要注释 以往,我们在代码中不写上几行注释,就会被认为是钟不负责任的态度.现在,这种观点正在改变.试想,如果我们所有的命名全部采用有意义的单词或词组,注释还有多少存在的价值. ...
- 编写高质量代码改善C#程序的157个建议——建议151:使用事件访问器替换公开的事件成员变量
建议151:使用事件访问器替换公开的事件成员变量 事件访问器包含两部分内容:添加访问器和删除访问器.如果涉及公开的事件字段,应该始终使用事件访问器.代码如下所示: class SampleClass ...
- 编写高质量代码改善C#程序的157个建议——建议150:使用匿名方法、Lambda表达式代替方法
建议150:使用匿名方法.Lambda表达式代替方法 方法体如果过小(如小于3行),专门为此定义一个方法就会显得过于繁琐.比如: static void SampeMethod() { List< ...
随机推荐
- 【转】Jmeter做功能测试的优点和不足
利用Jmeter做功能测试有以下优点: ● 不依赖于界面,如果服务正常启动,传递参数明确就可以添加测试用例,执行测试 ● 测试脚本不需要编程,熟悉http请求,熟悉业务流程,就可以根据页面 ...
- Java-Runoob:Java 简介
ylbtech-Java-Runoob:Java 简介 1.返回顶部 1. Java 简介 Java是由Sun Microsystems公司于1995年5月推出的Java面向对象程序设计语言和Java ...
- thinkphp中的验证码的实现
1.php端生成验证码函数 public function verify(){ // 验证码 import("@.Util.Image"); Image::buildImageVe ...
- Secure CRT修改文件夹的颜色
secureCRT有一个很大的问题是,如果设置Emulation Terminal 为Linux模式,则ls的时候,目录的蓝色跟背景的黑色非常接近,很难看清楚,修改办法 option->Glob ...
- Android BindService中遇到的一个小问题
今天在使用BindService的时候遇到个小问题,我希望通过Bindservice获取到这个服务,然后执行服务内的某个自定义方法,如下: if(bindService==null){ Intent ...
- Py修行路 python基础 (十九)面向对象进阶(下)
item系列 __slots__方法 __next__ 和 __iter__实现迭代器 析构函数 上下文管理协议 元类一.item系列 把对象操作属性模拟成字典的格式. 例如:对象名['key'] ...
- php学习之if
<html> <head> <title>xxx</title> <style> #tian{ color:blue; float:left ...
- easyUI datagrid表格添加“暂无记录”显示
扩展grid的onAfterRender事件 var myview = $.extend({}, $.fn.datagrid.defaults.view, { onAfterRender: f ...
- leetcode709
string toLowerCase(string str) { transform(str.begin(), str.end(), str.begin(), ::tolower); return s ...
- Document.location.href和.replace的区别
转自:https://www.cnblogs.com/GT_Andy/archive/2007/10/31/1922138.html 1 Document.location.href和.replace ...