Cleaner, more elegant, and harder to recognize (msdn blog)
It appears that some people interpreted the title of one of my rants from many months ago, "Cleaner, more elegant, and wrong", to be a reference to exceptions in general. (See bibliography reference [35]; observe that the citer even changed the title of my article for me!)
The title of the article was a reference to a specific code snippet that I copied from a book, where the book's author claimed that the code he presented was "cleaner and more elegant". I was pointing out that the code fragment was not only cleaner and more elegant, it was also wrong.
You can write correct exception-based programming.
Mind you, it's hard.
On the other hand, just because something is hard doesn't mean that it shouldn't be done.
Here's a breakdown:
| Really easy | Hard | Really hard |
|---|---|---|
| Writing bad error-code-based code Writing bad exception-based code |
Writing good error-code-based code | Writing good exception-based code |
It's easy to write bad code, regardless of the error model.
It's hard to write good error-code-based code since you have to check every error code and think about what you should do when an error occurs.
It's really hard to write good exception-based code since you have to check every single line of code (indeed, every sub-expression) and think about what exceptions it might raise and how your code will react to it. (In C++ it's not quite so bad because C++ exceptions are raised only at specific points during execution. In C#, exceptions can be raised at any time.)
But that's okay. Like I said, just because something is hard doesn't mean it shouldn't be done. It's hard to write a device driver, but people do it, and that's a good thing.
But here's another table:
| Really easy | Hard | Really hard | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
|
Here's some imaginary error-code-based code. See if you can classify it as "bad" or "not-bad":
BOOL ComputeChecksum(LPCTSTR pszFile, DWORD* pdwResult)
{
HANDLE h = CreateFile(pszFile, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE hfm = CreateFileMapping(h, NULL, PAGE_READ, 0, 0, NULL);
void *pv = MapViewOfFile(hfm, FILE_MAP_READ, 0, 0, 0);
DWORD dwHeaderSum;
CheckSumMappedFile(pvBase, GetFileSize(h, NULL),
&dwHeaderSum, pdwResult);
UnmapViewOfFile(pv);
CloseHandle(hfm);
CloseHandle(h);
return TRUE;
}
This code is obviously bad. No error codes are checked. This is the sort of code you might write when in a hurry, meaning to come back to and improve later. And it's easy to spot that this code needs to be improved big time before it's ready for prime time.
Here's another version:
BOOL ComputeChecksum(LPCTSTR pszFile, DWORD* pdwResult)
{
BOOL fRc = FALSE;
HANDLE h = CreateFile(pszFile, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h != INVALID_HANDLE_VALUE) {
HANDLE hfm = CreateFileMapping(h, NULL, PAGE_READ, 0, 0, NULL);
if (hfm) {
void *pv = MapViewOfFile(hfm, FILE_MAP_READ, 0, 0, 0);
if (pv) {
DWORD dwHeaderSum;
if (CheckSumMappedFile(pvBase, GetFileSize(h, NULL),
&dwHeaderSum, pdwResult)) {
fRc = TRUE;
}
UnmapViewOfFile(pv);
}
CloseHandle(hfm);
}
CloseHandle(h);
}
return fRc;
}
This code is still wrong, but it clearly looks like it's trying to be right. It is what I call "not-bad".
Now here's some exception-based code you might write in a hurry:
NotifyIcon CreateNotifyIcon()
{
NotifyIcon icon = new NotifyIcon();
icon.Text = "Blah blah blah";
icon.Visible = true;
icon.Icon = new Icon(GetType(), "cool.ico");
return icon;
}
(This is actual code from a real program in an article about taskbar notification icons, with minor changes in a futile attempt to disguise the source.)
Here's what it might look like after you fix it to be correct in the face of exceptions:
NotifyIcon CreateNotifyIcon()
{
NotifyIcon icon = new NotifyIcon();
icon.Text = "Blah blah blah";
icon.Icon = new Icon(GetType(), "cool.ico");
icon.Visible = true;
return icon;
}
Subtle, isn't it.
It's easy to spot the difference between bad error-code-based code and not-bad error-code-based code: The not-bad error-code-based code checks error codes. The bad error-code-based code never does. Admittedly, it's hard to tell whether the errors were handled correctly, but at least you can tell the difference between bad code and code that isn't bad. (It might not be good, but at least it isn't bad.)
On the other hand, it is extraordinarily difficult to see the difference between bad exception-based code and not-bad exception-based code.
Consequently, when I write code that is exception-based, I do not have the luxury of writing bad code first and then making it not-bad later. If I did that, I wouldn't be able to find the bad code again, since it looks almost identical to not-bad code.
My point isn't that exceptions are bad. My point is that exceptions are too hard and I'm not smart enough to handle them. (And neither, it seems, are book authors, even when they are trying to teach you how to program with exceptions!)
(Yes, there are programming models like RAII and transactions, but rarely do you see sample code that uses either.)
Cleaner, more elegant, and harder to recognize (msdn blog)的更多相关文章
- Cleaner, more elegant, and harder to recognize(翻译)
Cleaner, more elegant, and harder to recognize 更整洁,更优雅,但更难识别 看来,有些人把我几个月前一篇文章的标题"Cleaner,more e ...
- Cleaner, more elegant, and wrong(msdn blog)
Cleaner, more elegant, and wrong Just because you can't see the error path doesn't mean it doesn't e ...
- Cleaner, more elegant, and wrong(翻译)
Cleaner,more elegant,and wrong 整洁,更优雅,但是错的 并不是因为你看不到错误的产生路径就意味着它不存在. 下面是C#编程书中的一个片段,摘自关于异常处理的章节. try ...
- Go 开发关键技术指南 | 敢问路在何方?(内含超全知识大图)
作者 | 杨成立(忘篱) 阿里巴巴高级技术专家 Go 开发关键技术指南文章目录: 为什么你要选择 Go? Go 面向失败编程 带着服务器编程金刚经走进 2020 年 敢问路在何方? Go 开发指南大图 ...
- Go 开发关键技术指南 | Go 面向失败编程 (内含超全知识大图)
作者 | 杨成立(忘篱) 阿里巴巴高级技术专家 关注"阿里巴巴云原生"公众号,回复 Go 即可查看清晰知识大图! 导读:从问题本身出发,不局限于 Go 语言,探讨服务器中常常遇到的 ...
- diff/merge configuration in Team Foundation - common Command and Argument values - MSDN Blogs
One of the extensibility points we have in Team Foundation V1 is that you can configure any other di ...
- C# Development 13 Things Every C# Developer Should Know
https://dzone.com/refcardz/csharp C#Development 13 Things Every C# Developer Should Know Written by ...
- EF 5 最佳实践白皮书
Performance Considerations for Entity Framework 5 By David Obando, Eric Dettinger and others Publish ...
- Build Instructions (Windows) – The Chromium Projects
转自:http://121.199.54.6/wordpress/?p=1156 原始地址:http://www.chromium.org/developers/how-tos/build-instr ...
随机推荐
- 解决tomcat部署包错误
Context namespace element 'annotation-config' and its parser class [org.springframework.context.anno ...
- Jackson将json string转为Object,org.json读取json数组
从json文件读取json string或者自定义json string,将其转为object.下面采用的object为map,根据map读取json的某个数据,可以读取第一级的数据name,后来发现 ...
- pku 1330 Nearest Common Ancestors LCA离线
pku 1330 Nearest Common Ancestors 题目链接: http://poj.org/problem?id=1330 题目大意: 给定一棵树的边关系,注意是有向边,因为这个WA ...
- nignx 测试配置文件
nginx -t nginx: the configuration file /usr/local/nginx-1.2.9/conf/nginx.conf syntax is ok nginx: co ...
- 【NOIP2016提高组】愤怒的小鸟
https://www.luogu.org/problem/show?pid=2831 BFS 看到N这么小就可以想到搜索,求最少步数显然应该用BFS. 在这题中过两猪可以唯一确定一条抛物线,每一步可 ...
- 【NOIP2015提高组】子串
https://daniu.luogu.org/problem/show?pid=2679 看到方案数问题直觉就能想到DP,考虑用f(i,j,k)表示A[1...i]取k个子串组成B[1...j]的方 ...
- Android Toolbar 标题居中及字体样式自定义
首先找到 toolbar 所在xml布局文件,将toolbar控件放置在一个相对布局(RelativeLayout)中,然后设置toolbar的title为空(随便打几个空格即可): 然后在 tool ...
- OpenXml读取word内容(二)
注意事项 上一篇已经说明,这次就不一一说了,直接来正文: word内容 相关代码 方法1 static void Main(string[] args) { string wordPathStr = ...
- 《iOS Human Interface Guidelines》——Multitasking
多任务处理 多任务处理让人们在屏幕上(以及合适的iPad模式)查看多个app,而且在近期使用的app中高速地切换. 在iOS 9中.人们能够使用多任务处理UI(例如以下所看到的)来选择一个近期使用的a ...
- HDoj-1042 大数阶乘
N! Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submis ...