银河
SKYIV STUDIO
博客园
::
首页
:: :: ::
订阅
::
管理
::
105 随笔 :: 2 文章 :: 753 评论 :: 22 引用
C#中属性的一个容易搞错的问题
猜猜看以下程序的输出是什么?
using
System;
using
System.Windows.Forms;
namespace
Skyiv.Ben.Test
{
sealed
class
MainTest
{
static
void
Main()
{
Label lblOut
=
new
Label();
lblOut.Text
=
null
;
Console.WriteLine(
"
{0} {1}
"
, lblOut.Text
==
null
, lblOut.Text
==
""
);
}
}
}
该程序的输出是: False True,而不是: True False。也就是说,你将一个null赋值给lblOut.Text,而lblOut.Text的值是""(即string.Empty),而不是null。lblOut.Text是一个属性,实际上,按C#语法,给属性赋值是调用它的set方法,而获取属性的值是调用它的get方法,这两者不必一致,也就是说,你给属性赋一个值,然后再读该属性的值,取到的值就有可能不是你刚刚赋给它的值了。编程时如不小心,就有可能出现BUG。我以前写程序时就出现过这样的错误:
lblOut.Text = GetAccount();
if (lblOut.Text == null) lblOut.Text = "无此账号";
结果这个if语句永远不会取真值。
posted on 2005-09-22 20:59
银河
阅读(2279)
评论(18)
编辑
收藏
所属分类:
C# Base
评论
#1楼
2005-09-22 21:36
ocean2000 [未注册用户]
嘿嘿 ,明白点心动西
回复
引用
#2楼
2005-09-22 21:43
怀沙
呵呵。。。这一点的确是容易搞错
不过在实际运用中。。很少有人会写lblOut.Text = null;这样的语句。
回复
引用
查看
#3楼
[
楼主
]
2005-09-22 21:59
银河
@怀沙:
如我在正文中提到的, 在实际运用中, 我是写: lblOut.Text = GetAccount();
而 GetAccount() 方法在账号不存在时就返回null, 导致接下来的if语句失败.
回复
引用
查看
#4楼
[
楼主
]
2005-09-22 22:01
银河
@ocean2000:
> 嘿嘿 ,明白点心动西
你是不是想说: 明白点新东西?
回复
引用
查看
#5楼
2005-09-22 22:06
negy [未注册用户]
碰到过这个问题
回复
引用
#6楼
2005-09-23 09:28
yicone
很实际,谢谢
回复
引用
查看
#7楼
2005-09-23 09:28
疾风
似乎GetAccount()中,当帐号不存在时抛出异常是更好的处理办法
回复
引用
查看
#8楼
2005-09-23 09:29
wny
看看源码就知道原因了:
public virtual string Text
{
get
{
......
if (this.text != null)
{
return this.text;
}
return "";
}
set
{
if (value == null)
{
value = "";
}
......
}
}
回复
引用
查看
#9楼
2005-09-23 09:50
[ IceSharK - PP.Poet ]
呵呵 Reflector.exe 一下就知道了
public virtual string get_Text()
{
if (!this.GetStyle(ControlStyles.CacheText))
{
return this.WindowText;
}
if (this.text != null)
{
return this.text;
}
return "";
}
回复
引用
查看
#10楼
2005-09-23 09:58
杨波 [未注册用户]
微软所有字符串属性都有对null处理。再说了,那有你这样写代码的。
通用写法:
string str = GetAccount();
//1
lblOut.Text = (str == null) ? "无此账号" : str;
//2
if(str == null)
{
lblOut.Text = "无此账号";
}
else
{
lblOut.Text = str;
}
回复
引用
#11楼
2005-09-23 10:57
birdshome
在Fx 1.1里面判断字符串是否为空,以该是:if ( str != null && str.Length > 0 )
在Fx 2.0里面判断字符串是否为空,以该是:if ( !String.IsNullOrEmpty(str) )
回复
引用
查看
#12楼
[
楼主
]
2005-09-23 14:13
银河
@疾风
> 似乎GetAccount()中,当帐号不存在时抛出异常是更好的处理办法
这是个设计权衡的问题,有时候是应该抛出异常,但有时候返回null值会更好些,要看大多数情况下的调用者的应用场合。如果在这个例子中,GetAccount()当账号不存在时抛出异常的话,调用者就必须使用try...catch语句块了,在这个场合来说,不是很好的设计。
回复
引用
查看
#13楼
[
楼主
]
2005-09-23 14:18
银河
@杨波
> 再说了,那有你这样写代码的。
如果不是Text属性把null值转换为""值,为什么不能这样写代码?至少,可以少使用一个临时变量。我提出这个问题,只不过是给大家提个醒,省得出现这种BUG。
回复
引用
查看
#14楼
2005-09-23 15:56
小陆
禁止使用“==”判断两个字符串是否相同。
“==”只能判断两个字符串的“引用”是否相同。
要使用equals方法,如果不判断字符串是否为null,equals自然会出现异常,不存在隐藏的bug。
回复
引用
查看
#15楼
[
楼主
]
2005-09-23 16:10
银河
@小陆
> 禁止使用“==”判断两个字符串是否相同。
> “==”只能判断两个字符串的“引用”是否相同。
完全可以使用“==”判断两个字符串是否相同。因为System.String类已经重载了“==”方法,从而判断两个字符串的内容是否相同,而不是其“引用”是否相同。如果不是这样的话,我在正文中的程序也就不可能输出: False True 了,事实证明,lblOut.Text == "" 的值是True,而不是False。如果“==”是比较“引用”是否相同的话,lblOut.Text == ""的值应该是False。
回复
引用
查看
#16楼
2005-09-23 21:13
yuxs [未注册用户]
@小陆
由于string是使用量非常大的数据类型,所以为了编码方便,string在C#/.NET中进行了特别处理,可以看作是一个能够当作值类型使用的引用类型
回复
引用
#17楼
[
楼主
]
2005-09-29 11:39
银河
@小陆
> 禁止使用“==”判断两个字符串是否相同。
> “==”只能判断两个字符串的“引用”是否相同。
补充说明如下:
《.NET本质论-第1卷:公共语言运行库》,Don Box, Chris Sells著,张晓坤译,中国电力出版社,2004年4月第1版
“第5章 实例”,“相等与同一”小节,pp.127-128:
如果不考虑编程语言的特殊性,则很难说清楚同一性和相等性。在C++和C#中,标准的比较操作符是==和!=。当这些操作符被应用在基本数据类型上时,它们只是简单地发射直接比较这两个值的CIL指令。当被应用在对象引用上时,这些操作符发射的CIL指令原则上等价于调用System.Object.ReferenceEquals方法。然而,C++和C#都支持操作符重载,这意味首一个特定的类型可能将==(和!=)操作符映射到任意代码。典型的例子就是System.String类型。System.String类型重载了这些操作符,使之调用Equals方法。于是,对于字符串比较,就能采用更为直观的相等比较。一般来说,重写Equals方法的类型应该考虑重载==和!=操作符,尤其当类型是(或者其行为像)值类型时。
回复
引用
查看
#18楼
2005-11-03 10:04
小陆
==和equals是决不相同的,不管他重载不重载。
string s = null;
string n = null;
这时候==返回true,而equals会出现异常。
null从逻辑上表示“未知”,两个“未知”是不应该相同的。使用==来判断两个字符串,可能会掩盖程序中的错误。
写程序如果有错误,最好是在编译的时候出错,其次是在运行的时候迅速出错。这个时候的错误出现的越多,程序最后就是越强壮的。
回复
引用
查看
#19楼
[
楼主
]
2005-11-03 11:47
银河
@小陆
小陆的评论之一
禁止使用“==”判断两个字符串是否相同。
“==”只能判断两个字符串的“引用”是否相同。
要使用equals方法,如果不判断字符串是否为null,equals自然会出现异常,不存在隐藏的bug。
小陆的评论之二
==和equals是决不相同的,不管他重载不重载。
string s = null;
string n = null;
这时候==返回true,而equals会出现异常。
null从逻辑上表示“未知”,两个“未知”是不应该相同的。使用==来判断两个字符串,可能会掩盖程序中的错误。
写程序如果有错误,最好是在编译的时候出错,其次是在运行的时候迅速出错。这个时候的错误出现的越多,程序最后就是越强壮的。
首先,你在评论中提到的“equals”都应该改为“Equals”,因为C#是区分大小字母的。
其次,我不同意你“评论之一”中的如下观点:
> 禁止使用“==”判断两个字符串是否相同。
> “==”只能判断两个字符串的“引用”是否相同。
理由是:
Don Box, Chris Sells 说
当被应用在对象引用上时,这些操作符(标准的比较操作符==和!=)发射的CIL指令
原则上
等价于调用System.Object.ReferenceEquals方法。然而,C++和C#都支持操作符重载,这意味首一个特定的类型可能将==(和!=)操作符映射到任意代码。典型的例子就是System.String类型。
System.String类型重载了这些操作符,
使之调用Equals方法。
于是,对于字符串比较,就能采用
更为直观的相等比较
。
一般来说,重写Equals方法的类型应该考虑重载==和!=操作符,尤其当类型是(或者其行为像)值类型时。
而对于你的“评论之二”中的观点基本上我都赞同。
但使用“==”来判断两个字符串是否相同是非常方便和直观的(在实践中也是被广泛采用的),而且实际上和使用“Equals”方法差别非常小(其差别就是你在“评论之二”中提到的),我认为在大多数情况上不会造成影响。
我们用“
Lutz Roeder's .NET Reflector
”来看一下 System.String 的源代码:
System.String.op_Equality(String, String) : Boolean
public static bool operator ==(string a, string b)
{
return string.Equals(a, b);
}
System.String.Equals(String, String) : Boolean
public static bool Equals(string a, string b)
{
if (a == b) // IL Code: bne.un.s L_0006
{
return true;
}
if ((a != null) && (b != null))
{
return string.EqualsHelper(a, b);
}
return false;
}
System.String.Equals(String) : Boolean
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public bool Equals(string value)
{
if ((value == null) && (this != null))
{
return false;
}
return string.EqualsHelper(this, value);
}
System.String.EqualsHelper(String, String) : Boolean
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
private static unsafe bool EqualsHelper(string strA, string strB)
{
int num1 = strA.Length;
if (num1 != strB.Length)
{
return false;
}
fixed (char* local1 = strA)
{
char* chPtr1 = local1;
fixed (char* local2 = strB)
{
char* chPtr2 = local2;
char* chPtr3 = chPtr1;
char* chPtr4 = chPtr2;
while (num1 >= 10)
{
if ((((*(((int*) chPtr3)) != *(((int*) chPtr4))) || (*(((int*) (chPtr3 + 2))) != *(((int*) (chPtr4 + 2))))) || ((*(((int*) (chPtr3 + 4))) != *(((int*) (chPtr4 + 4)))) || (*(((int*) (chPtr3 + 6))) != *(((int*) (chPtr4 + 6)))))) || (*(((int*) (chPtr3 + 8))) != *(((int*) (chPtr4 + 8)))))
{
break;
}
chPtr3 += 10;
chPtr4 += 10;
num1 -= 10;
}
while (num1 > 0)
{
if (*(((int*) chPtr3)) != *(((int*) chPtr4)))
{
break;
}
chPtr3 += 2;
chPtr4 += 2;
num1 -= 2;
}
return (num1 <= 0);
}
}
}
可见它们最终都是调用“System.String.EqualsHelper(String, String) : Boolean”方法来判断两个字符串是否相同。
回复
引用
查看
新用户注册
刷新评论列表
标题
姓名
主页
Email
(博主才能看到)
验证码
*
看不清,换一张
[
登录
][
注册
]
内容(请不要发表任何与政治相关的内容)
网站首页
新闻频道
社区
小组
博问
网摘
闪存
找找看
Remember Me?
登录
使用高级评论
新用户注册
返回页首
恢复上次提交
[使用Ctrl+Enter键可以直接提交]
该文被作者在 2005-10-07 17:52 编辑过
"五向定位"职业成长路线公开课(上海、南京、大连)
Google站内搜索
相关文章:
C#字符串分割
如何配置C#命令行编译器
关于IE问题,请教和求救
解决高分辨率看网页字体太小问题的一个方法
FileInfo 复制文件 是否完成 如何判断 C#
C# 外网如何用TCP向内网传文件?
C# 来这里学习C#吧
C# 委托
相关链接:
所属分类的其他文章:
C#解惑34: 被计数击倒了
C#谜题34: 被计数击倒了
C#解惑27: 变幻莫测的i值
C#谜题27: 变幻莫测的i值
C#解惑26: 在循环中
C#谜题26: 在循环中
C#解惑86: 有害的括号垃圾
C#谜题86: 有害的括号垃圾
C#解惑04: 初级问题
C#谜题04: 初级问题
最新IT新闻:
Silverlight对Flash 微软打垮Adobe
IBM扩大研究规模 在上海成立研究院
eWeek评Google八大“20%项目”
微软终于发布Silverlight 2正式版
微软首次打破先例 Windows 7不再改名
<
2005年9月
>
日
一
二
三
四
五
六
28
29
30
31
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
1
2
3
4
5
6
7
8
与我联系
发短消息
搜索
常用链接
我的随笔
我的空间
我的短信
我的评论
更多链接
我的参与
我的新闻
最新评论
我的标签
留言簿
(24)
给我留言
查看留言
我参与的团队
Windows Mobile 应用开发(0/604)
CLR基础研究团队(1/413)
随笔分类
(105)
.NET Compact Framework(25)
(rss)
.NET Framework(11)
(rss)
ADO.NET(3)
(rss)
C# Base(25)
(rss)
Windows Form(3)
(rss)
其他(6)
(rss)
设计模式(1)
(rss)
算法(31)
(rss)
随笔档案
(105)
2008年8月 (1)
2008年7月 (14)
2008年6月 (14)
2008年4月 (1)
2007年11月 (1)
2007年10月 (12)
2007年9月 (7)
2007年8月 (11)
2007年1月 (1)
2006年12月 (1)
2006年9月 (23)
2005年11月 (1)
2005年10月 (10)
2005年9月 (6)
2005年8月 (2)
文章分类
(2)
C# Base(1)
(rss)
Other(1)
(rss)
收藏夹
(8)
ASP.NET(5)
(rss)
C# Base(2)
(rss)
工具软件(1)
(rss)
.NET
BigInteger(C#)
CodeKeep
Downloads
Mono for Debian
MSDN for VS2005
MSDN Library
MSDN 开发中心
NAnt
NDoc
P/Invoke
VS2005 Home
Blogs
Allen Lee
Matrix67
Michelle
Sumtec
博客堂
花开花落
空间/IV
Database
ODP.NET Download
Oracle .NET 开发人员中心
Oracle Database 10g Downloads
Oracle Home
Oracle 数据库管理员备忘录
PostgreSQL 中文网
SharpHSQL
SQLite Home
Java
Thinking in Java
Linux
ChinaUnix
Debian Home
GIMP
LinuxSir
中国Linux公社
Other
7-Zip
DOS
insidewindows
Scilab Home
数学研发网
维基百科
Python
Dive Into Python
Iron Python
IronPython Workspace
Python - 中文Zope用户组
Python Home
Python中文社区
XHTML
网页设计师
积分与排名
积分 - 192374
排名 - 180
最新评论
1. re: 使用 C# 开发智能手机软件:推箱子(一)
你好,能不能邮件发一份源代码给我啊,这个地址好像下不了
--山阴
2. re: 数据库小工具(C#)
楼主能在更新一下吗?现在好像下不了了!期待您的更新....可以发我邮箱里。谢谢
--民工
3. re: 用 XmlReader 读取 Excel 2007 文件
@张磊.NET (29楼)
现在可以下载了。
--银河
4. re: 用 XmlReader 读取 Excel 2007 文件
@银河
1楼的链接下载不了,请楼主帮忙
我的邮箱是zhangleipub@126.com,能否给我发一份源码,多谢!
--张磊.NET
5. 数独知识
这题怎写? xxx657xxx x93xx1xxx 5xxxxxxx1 8x6xx5xxx xx7xxxx8x xxxxxx629 x49x2xxxx xxxxxx4xx xxxx138xx 我急着要...
--紫色光芒
阅读排行榜
1. 计算机语言发展史(8204)
2. Microsoft .NET Framework 的版本(7455)
3. 数独解算器(ASP.NET 2.0)(6799)
4. 在 Linux 下运行 ASP.NET 2.0(6612)
5. 使用 C# 开发智能手机软件:推箱子(一)(5881)
评论排行榜
1. 使用 C# 开发智能手机软件:推箱子(一)(44)
2. Microsoft .NET Framework 的版本(44)
3. 使用 C# 开发智能手机软件:推箱子(37)
4. 可以使用C#语言的在线ACM题库(32)
5. 在 Linux 下运行 ASP.NET 2.0(31)
Powered by:
博客园
Copyright © 银河