C# C中子字符串的意外行为#
.netC# C中子字符串的意外行为#,c#,.net,string,substring,clr,C#,.net,String,Substring,Clr,.netSystem.String类中Substring()方法的定义如下 public string Substring(int startIndex) 其中,startIndex是根据方法定义的“此实例中子字符串的从零开始的字符位置”。如果我理解正确,这意味着它将给我字符串的一部分,从给定的从零开始的索引开始 现在,如果我有一个字符串“ABC”,并使用不同的索引获取子字符串,我会得到以下结果 var str = "ABC"; var chars = str.ToArray(); //ret
System.String
类中Substring()
方法的定义如下
public string Substring(int startIndex)
其中,startIndex
是根据方法定义的“此实例中子字符串的从零开始的字符位置”。如果我理解正确,这意味着它将给我字符串的一部分,从给定的从零开始的索引开始
现在,如果我有一个字符串“ABC”,并使用不同的索引获取子字符串,我会得到以下结果
var str = "ABC";
var chars = str.ToArray(); //returns 3 char 'A', 'B', 'C' as expected
var sub2 = str.Substring(2); //[1] returns "C" as expected
var sub3 = str.Substring(3); //[2] returns "" ...!!! Why no exception??
var sub4 = str.Substring(4); //[3] throws ArgumentOutOfRangeException as expected
为什么它不为案例[2]抛出异常
字符串有3个字符,因此索引是[0,1,2]
,甚至ToArray()
,tocharray()
方法按预期返回3个字符!如果我尝试使用起始索引3
来Substring()
,它不应该抛出异常吗?C中的所有字符串最后都有字符串。空的
关于这个问题
来自MSDN-类别(系统):
在.NET Framework中,字符串对象可以包含嵌入的null
字符,作为字符串长度的一部分。然而,在
一些语言如C和C++,空字符表示结束。
一根绳子的长度;它不被视为字符串的一部分,也不是
作为字符串长度的一部分计算
这是一种非常明确的正确行为:
返回值:一个字符串,该字符串相当于此实例中以startIndex开头的子字符串,如果startIndex等于此实例的长度,则为空。
如果startIndex
小于零或*大于此实例的长度,则抛出ArgumentOutOfRangeException
*强>
换句话说,从最后一个字符开始的子字符串将得到一个空字符串
您希望它提供字符串的一部分的注释与此不兼容。“字符串的一部分”也包括长度为零的所有子字符串集,这一点可以从以下事实得到证明:
s.substring(n,0)
也将给出一个空字符串。基于MSDN上写的内容:
*
返回值-一个字符串,相当于此实例中以startIndex开头的子字符串,如果startIndex等于此实例的长度,则为空
例外情况
ArgumentOutOfRangeException-startIndex小于零或大于此实例的长度
*查看文档,如果开始索引等于长度,将返回一个空字符串
一个字符串,其长度等于
在此实例中,从startIndex开始,如果startIndex相等,则为空
到该实例的长度,且长度为零
子字符串的作用是检查startIndex是否大于字符串的长度,然后抛出异常。在您的情况下,它是相等的(字符串长度为3)。然后检查子字符串的长度是否为零,以及是否为,并返回String.Empty。在您的例子中,子字符串的长度是字符串(3)的长度减去startIndex(3)。这就是为什么子字符串的长度为0并且返回空字符串的原因。: 首先,这被称为:
public string Substring(int startIndex)
{
return this.Substring(startIndex, this.Length - startIndex);
}
由于值的减法,长度为0:
public string Substring(int startIndex, int length)
{
if (startIndex < 0)
{
throw new ...
}
if (startIndex > this.Length)
{
throw new ...
}
if (length < 0)
{
throw new ...
}
if (startIndex > (this.Length - length))
{
throw new ...
}
if (length == 0) // <-- NOTICE HERE
{
return Empty;
}
if ((startIndex == 0) && (length == this.Length))
{
return this;
}
return this.InternalSubString(startIndex, length);
}
公共字符串子字符串(int startIndex,int length)
{
如果(起始索引<0)
{
扔新的。。。
}
如果(startIndex>this.Length)
{
扔新的。。。
}
如果(长度<0)
{
扔新的。。。
}
if(startIndex>(this.Length-Length))
{
扔新的。。。
}
如果(length==0)/这里有很多技术上的答案说明框架是如何处理方法调用的,但是我想通过类比来说明为什么它是这样的
将字符串
视为围栏,其中围栏面板本身是字符,由编号如下的围栏柱支撑:
0 1 2 3
| A | B | C | "ABC"
0 1 2 3 4 5 6 7 8 9
| M | y | | S | t | r | i | n | g | "My String"
在这个类比中,string.Substring(n)
返回以fencepostn
开头的面板的string
。请注意,字符串的最后一个字符后面有一个fencepost。使用此fencepost调用函数会返回一个值,说明在该点之后没有fencepanel(即,它返回空的字符串
)
类似地,string.Substring(n,l)
返回以fencepostn
开头的l
面板的字符串。这就是为什么类似于“ABC”的东西。Substring(2,0)
也返回”
。为了补充其他答案,Mono也正确地实现了这种行为
public String Substring (int startIndex)
{
if (startIndex == 0)
return this;
if (startIndex < 0 || startIndex > this.length)
throw new ArgumentOutOfRangeException ("startIndex");
return SubstringUnchecked (startIndex, this.length - startIndex);
}
// This method is used by StringBuilder.ToString() and is expected to
// always create a new string object (or return String.Empty).
internal unsafe String SubstringUnchecked (int startIndex, int length)
{
if (length == 0)
return String.Empty;
string tmp = InternalAllocateStr (length);
fixed (char* dest = tmp, src = this) {
CharCopy (dest, src + startIndex, length);
}
return tmp;
}
公共字符串子字符串(int startIndex)
{
如果(startIndex==0)
归还这个;
if(startIndex<0 | | startIndex>this.length)
抛出新ArgumentOutOfRangeException(“startIndex”);
返回未选中的子字符串(startIndex,this.length-startIndex);
}
//此方法由StringBuilder.ToString()使用,并应
//始终创建新字符串对象(或返回string.Empty)。
未选中内部不安全字符串子字符串(int startIndex,int length)
{
如果(长度==0)
返回字符串。空;
字符串tmp=internalallocatest(长度);
固定(char*dest=tmp,src=this){
字符拷贝(dest、src+startIndex、长度);
}
返回tmp;
}
如您所见,如果长度等于零,它将返回String.Empty。可能是一个\0
字符(用于标记字符串的结尾)。但我不确定.NET是否使用了该字符。值得一提的是google thoughline 1246@Thank@AlexK.和其他(答案)用于指出实现和MSDN文档。我可以看到框架团队就是这样实现的,但对我(我猜也只有少数人)来说,这有点出乎意料!快速而肮脏的答案是:.NET知道0长度字符串的含义,它是d