您的位置:首页 > 编程语言 > C#

C#判断字符串是否为空的三种方法及其比较

2008-11-28 11:20 856 查看
1.最普遍、最直接的方法

string Input = "Yours Input Msg";

if (Input == "")

;

IL:

.locals init ([0] string Input,[1] bool CS$4$0000)

IL_0000: nop

IL_0001: ldstr "Yours Input Msg"

IL_0006: stloc.0

IL_0007: ldloc.0

IL_0008: ldstr ""

IL_000d: call bool [mscorlib]System.String::op_Equality(string,string)

2.使用String.Empty

string Input = "Yours Input Msg";

if (Input == String.Empty)

;

IL:

.locals init ([0] string Input,[1] bool CS$4$0000)

IL_0000: nop

IL_0001: ldstr "Yours Input Msg"

IL_0006: stloc.0

IL_0007: ldloc.0

IL_0008: ldsfld string [mscorlib]System.String::Empty

IL_000d: call bool [mscorlib]System.String::op_Equality(string,string)

3.使用string.Length

string Input = "Yours Input Msg";

if (Input.Length == 0)

;

IL:

.locals init ([0] string Input,[1] bool CS$4$0000)

IL_0000: nop

IL_0001: ldstr "Yours Input Msg"

IL_0006: stloc.0

IL_0007: ldloc.0

IL_0008: callvirt instance int32 [mscorlib]System.String::get_Length()

在看过了三种方法之后,我们从底层开始进行比较,这里先做个假设,我们假设所有的单个IL语句执行时间相等,单位为1。(其实还是有差异的,但并不影响我们作比较。)

我们先反汇编mscorlib.dll得到System.String::op_Equality(strin,string):

.method public hidebysig specialname static

bool op_Equality(string a,

string b) cil managed

{

// 代码大小 8 (0x8)

.maxstack 8

IL_0000: ldarg.0

IL_0001: ldarg.1

IL_0002: call bool System.String::Equals(string,

string)

IL_0007: ret

} // end of method String::op_Equality

发现他还要调用这个函数System.String::Equals(strin,string):

.method public hidebysig virtual instance bool

Equals(object obj) cil managed

{

.custom instance void System.Runtime.ConstrainedExecution.ReliabilityContractAttribute::.ctor(valuetype System.Runtime.ConstrainedExecution.Consistency,

valuetype System.Runtime.ConstrainedExecution.Cer) = ( 01 00 03 00 00 00 01 00 00 00 00 00 )

// 代码大小 23 (0x17)

.maxstack 2

.locals init (string V_0)

IL_0000: ldarg.1

IL_0001: isinst System.String

IL_0006: stloc.0

IL_0007: ldloc.0

IL_0008: brtrue.s IL_000f

IL_000a: ldarg.0

IL_000b: brfalse.s IL_000f

IL_000d: ldc.i4.0

IL_000e: ret

IL_000f: ldarg.0

IL_0010: ldloc.0

IL_0011: call bool System.String::EqualsHelper(string,

string)

IL_0016: ret

} // end of method String::Equals

然后还要调用System.String::EqualsHelper([b]string[/b],string):

.method private hidebysig static bool EqualsHelper(string strA,

string strB) cil managed

{

.custom instance void System.Runtime.ConstrainedExecution.ReliabilityContractAttribute::.ctor(valuetype System.Runtime.ConstrainedExecution.Consistency,

valuetype System.Runtime.ConstrainedExecution.Cer) = ( 01 00 03 00 00 00 01 00 00 00 00 00 )

// 代码大小 187 (0xbb)

.maxstack 3

.locals init (int32 V_0,

char* V_1,

char* V_2,

char* V_3,

char* V_4,

bool V_5,

string pinned V_6,

string pinned V_7)

IL_0000: ldarg.0

IL_0001: callvirt instance int32 System.String::get_Length()

IL_0006: stloc.0

IL_0007: ldloc.0

IL_0008: ldarg.1

IL_0009: callvirt instance int32 System.String::get_Length()

IL_000e: beq.s IL_0012

IL_0010: ldc.i4.0

IL_0011: ret

IL_0012: ldarg.0

IL_0013: stloc.s V_6

IL_0015: ldloc.s V_6

IL_0017: conv.i

IL_0018: dup

IL_0019: brfalse.s IL_0021

IL_001b: call int32 System.Runtime.CompilerServices.RuntimeHelpers::get_OffsetToStringData()

IL_0020: add

IL_0021: stloc.1

IL_0022: ldarg.1

IL_0023: stloc.s V_7

IL_0025: ldloc.s V_7

IL_0027: conv.i

IL_0028: dup

IL_0029: brfalse.s IL_0031

IL_002b: call int32 System.Runtime.CompilerServices.RuntimeHelpers::get_OffsetToStringData()

IL_0030: add

IL_0031: stloc.2

IL_0032: ldloc.1

IL_0033: stloc.3

IL_0034: ldloc.2

IL_0035: stloc.s V_4

IL_0037: br.s IL_008b

IL_0039: ldloc.3

IL_003a: ldind.i4

IL_003b: ldloc.s V_4

IL_003d: ldind.i4

IL_003e: bne.un.s IL_00a9

IL_0040: ldloc.3

IL_0041: ldc.i4.4

IL_0042: conv.i

IL_0043: add

IL_0044: ldind.i4

IL_0045: ldloc.s V_4

IL_0047: ldc.i4.4

IL_0048: conv.i

IL_0049: add

IL_004a: ldind.i4

IL_004b: bne.un.s IL_00a9

IL_004d: ldloc.3

IL_004e: ldc.i4.8

IL_004f: conv.i

IL_0050: add

IL_0051: ldind.i4

IL_0052: ldloc.s V_4

IL_0054: ldc.i4.8

IL_0055: conv.i

IL_0056: add

IL_0057: ldind.i4

IL_0058: bne.un.s IL_00a9

IL_005a: ldloc.3

IL_005b: ldc.i4.s 12

IL_005d: conv.i

IL_005e: add

IL_005f: ldind.i4

IL_0060: ldloc.s V_4

IL_0062: ldc.i4.s 12

IL_0064: conv.i

IL_0065: add

IL_0066: ldind.i4

IL_0067: bne.un.s IL_00a9

IL_0069: ldloc.3

IL_006a: ldc.i4.s 16

IL_006c: conv.i

IL_006d: add

IL_006e: ldind.i4

IL_006f: ldloc.s V_4

IL_0071: ldc.i4.s 16

IL_0073: conv.i

IL_0074: add

IL_0075: ldind.i4

IL_0076: bne.un.s IL_00a9

IL_0078: ldloc.3

IL_0079: ldc.i4.s 20

IL_007b: conv.i

IL_007c: add

IL_007d: stloc.3

IL_007e: ldloc.s V_4

IL_0080: ldc.i4.s 20

IL_0082: conv.i

IL_0083: add

IL_0084: stloc.s V_4

IL_0086: ldloc.0

IL_0087: ldc.i4.s 10

IL_0089: sub

IL_008a: stloc.0

IL_008b: ldloc.0

IL_008c: ldc.i4.s 10

IL_008e: bge.s IL_0039

IL_0090: br.s IL_00a9

IL_0092: ldloc.3

IL_0093: ldind.i4

IL_0094: ldloc.s V_4

IL_0096: ldind.i4

IL_0097: bne.un.s IL_00ad

IL_0099: ldloc.3

IL_009a: ldc.i4.4

IL_009b: conv.i

IL_009c: add

IL_009d: stloc.3

IL_009e: ldloc.s V_4

IL_00a0: ldc.i4.4

IL_00a1: conv.i

IL_00a2: add

IL_00a3: stloc.s V_4

IL_00a5: ldloc.0

IL_00a6: ldc.i4.2

IL_00a7: sub

IL_00a8: stloc.0

IL_00a9: ldloc.0

IL_00aa: ldc.i4.0

IL_00ab: bgt.s IL_0092

IL_00ad: ldloc.0

IL_00ae: ldc.i4.0

IL_00af: cgt

IL_00b1: ldc.i4.0

IL_00b2: ceq

IL_00b4: stloc.s V_5

IL_00b6: leave.s IL_00b8

IL_00b8: ldloc.s V_5

IL_00ba: ret

} // end of method String::EqualsHelper

粗略估算了下,System.String::op_Equality(strin,string)执行时间大概是160。

这样方法1的大概运行时间是165,空间大概是220字节。方法2比只多了一次调用System.String::Empty(程序中的静态变量)

.field public static initonly string Empty

因此运行运行时间基本同方法1一致,空间上节省了2字节(原因是方法1需要先建立一个string类型("")后,再进行判断),可以忽略不计。很多文章谈到C#判断空字符串不同方法性能的时候往往以此为据,而看过上面的IL之后,我却不这么认为,真正的性能差异体现在了System.String::op_Equality(string,string)和System.String::get_Length()这两个函数之上。

下面我们看一下System.String::get_Length():

.method public hidebysig specialname instance int32

get_Length() cil managed internalcall

{

} // end of method String::get_Length

这个函数的函数体居然是空的?为什么?那难道这个不起作用吗?我认为是cil managed internalcall这个起了作用,取得字符串(内存中一块连续的区域)长度是非常快的,可以由CPU直接完成(这个是我的猜测,目前关于internalcall我们仅仅知道internalcall在RuntimeType类里面则是很容易就能够找到,速度很快,性能很高,这个可能涉及到程序的编译原理,不懂~~~)

反正这个函数很快很快就是了,那么方法3的运行时间大概只有6,空间大概是20多。

这样,我们得到了一个令人吃惊的结论,方法3在时间上只有前两种方法的约1/28,空间约是1/9。

为什么这样,我也不明白,我希望高手来指出我叙述或者论证不当的地方。

那么我们是否可以就直接使用方法3了呢?

答案是否定的。在C#使用属性时一定要注意异常捕捉的问题。

String的Length属性返回此实例中Char对象的个数,在C#中字符串可以看成是由多个Char组成的字符数组,这样如果String为null的话问题就来了,当String置为null时,相当于一个没有实例化的字符串数组,用Length取一个没有实例化数组的长度,那么产生异常就在所难免了。

因此应该这么做:

string Input = "Yours Input Msg";

if (Input!=null && Input.Length==0)

;

看一下IL:

.locals init ([0] string Input,[1] bool CS$4$0000)

IL_0000: nop

IL_0001: ldstr "Yours Input Msg"

IL_0006: stloc.0

IL_0007: ldloc.0

IL_0008: brfalse.s IL_0018

IL_000a: ldloc.0

IL_000b: callvirt instance int32 [mscorlib]System.String::get_Length()

IL_0010: ldc.i4.0

IL_0011: ceq

IL_0013: ldc.i4.0

IL_0014: ceq

IL_0016: br.s IL_0019

运行时间大概是在5-13,空间越是30字节。这样,比起Input == ""这个方法,效率大约提高

13-30多倍(因为有短路运算),空间却只要1/7。

因此这些方法中判断字符串是否为空的最佳方法是:

desStr!=null && desStr.Length==0

本人初涉C#,很多东西掌握的都不是很好,如果你有自己的看法,请告知我,一起学习,一起进步:)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐