byte[]、sbyte[]、int[]以及Array的故事
byte[]、sbyte[]、int[]以及Array的故事
很久沒有搞比較底層一點(diǎn)的東西了,最近又開始搞,于是乎又發(fā)現(xiàn)了一些很雞毛蒜皮的事情。也許有人已經(jīng)發(fā)現(xiàn)過了,那就請?jiān)徫揖驮賮硗诰蛞槐椤?/p>
?
byte[]、sbyte[]、int[]等數(shù)組,是一種特殊的類型,他們都繼承自Array。不過這個(gè)繼承還不是一般的繼承關(guān)系,編譯器和CLI都做了一些特殊的工作。我們先看普通的繼承關(guān)系:
class Human
{
}
class Man : Human
{
}
class Woman : Human
{
}
?
如果我們試圖寫下列的代碼:
Human human = new Man();
Woman woman = (Woman)human;
?
那么結(jié)果是什么,你懂的。
?
好吧,你不懂。那我給說一下,Man和Woman都是從Human這個(gè)類繼承的,因此Man和Woman是兄妹關(guān)系,兄妹之間實(shí)際上是不能互相轉(zhuǎn)換的。雖然上面的代碼是可以編譯通過的,但是運(yùn)行起來就會(huì)拋異常,告訴你Man類型的對象是不能轉(zhuǎn)換成Woman的。
?
不過換到byte[]、sbyte[]等數(shù)組里面,就不是這么簡單了。比如說:
byte[] source = {1, 2, 3};
sbyte[] target = (sbyte[])(Array)source;
foreach(var item in target)
{
??? Console.WriteLine(item);
}
?
你猜怎么著?哎?為啥居然能跑呢?按道理byte[]和sbyte[]都是從Array派生的,那么他們之間自然也是兄妹關(guān)系了??磥鞢LI為我們做了一些事情。好了,那么當(dāng)我們有這么一個(gè)函數(shù):
static public void Copy(sbyte[] source, sbyte[] target)
{
??? if (source == null)
??? {
??????? throw new ArgumentNullException("source");
??? }
??? if (target == null)
??? {
??????? throw new ArgumentNullException("target");
??? }
??? int copyLength = Math.Min(source.Length, target.Length);
??? Array.Copy(source, target, copyLength);
}
?
然后這么調(diào)用:
sbyte[] target = GetTarget();
sbyte[] another = new sbyte[3];
Copy(target, another);
?
在Copy函數(shù)內(nèi)會(huì)出現(xiàn)除了ArgumentNullException之外的其它異常嗎?這個(gè)怎么看也是不能吧?錯(cuò)了,可能會(huì)出現(xiàn)InvalidCastException異常,假如GetTarget的代碼是:
?
sbyte[] GetTarget()
{
??? return (sbyte[])(Array)new byte[] {1, 2, 3};
}
?
?
?
這就奇怪了,為啥咧?因?yàn)锳rray.Copy會(huì)判斷要復(fù)制的數(shù)組,其元素類型之間是什么關(guān)系。這個(gè)還不是簡單的相等關(guān)系,說這個(gè)之前,我們再看另一個(gè)古怪的例子:
byte[] source = { 1, 2, 3 };
int[] intArray = (int[])(Array)source;
?
這個(gè)照樣還是能夠編譯的,不過運(yùn)行時(shí)會(huì)拋出來InvalidCastException。飛鴿傳書:http://www.freeeim.com/,為什么sbyte[]和byte[]之間可以互相轉(zhuǎn)換,同樣是兄妹關(guān)系的byte[]和int[]就不能互相轉(zhuǎn)換呢?其實(shí),這個(gè)問題不需要深究都會(huì)知道,sbyte和byte[]的元素所占字節(jié)數(shù)都一樣,所以理論上是可以直接轉(zhuǎn)換來訪問的。而int[]和byte[]之間,如果因?yàn)槠湓匾粋€(gè)是4字節(jié)另一個(gè)是1字節(jié),而導(dǎo)致不允許直接進(jìn)行轉(zhuǎn)換,那也很正常。不過,既然sbyte[]和byte[]之間都可以強(qiáng)制轉(zhuǎn)換,而且轉(zhuǎn)換之后都可以訪問元素,為啥就不能通過Array.Copy來復(fù)制呢?嗯,不用想,Array.Copy對于源數(shù)組和目標(biāo)數(shù)組的元素類型進(jìn)行了判斷,至于咋判斷的Reflector看不到,我們就先猜測吧:Array.Copy只要發(fā)現(xiàn)源和目標(biāo)數(shù)組的元素類型不一樣,那就不能夠復(fù)制。真是這樣嗎?我們再看另一個(gè)例子:
byte[] source = {1, 2, 3};
int[] intArray = new int[source.Length];
Array.Copy(source, intArray, source.Length);
foreach(var item in intArray)
{
??? Console.WriteLine(item);
}
?
?
怎么樣,這個(gè)代碼你覺得跑起來會(huì)如何?拋InvalidCastException?ArrayTypeMismatchException?其實(shí)什么都不會(huì)拋出來。原來,Array.Copy檢驗(yàn)的條件是,如果源和目標(biāo)數(shù)組的元素類型是內(nèi)置的值類型,只要能做寬轉(zhuǎn)換(widening conversion)那就可以成功復(fù)制。比如byte->int,反之就會(huì)拋異常。而用戶自定義的值類型就沒有這個(gè)優(yōu)惠政策了,哪怕你重寫了類型轉(zhuǎn)換操作符,或者實(shí)現(xiàn)了IConvertible,都不行。當(dāng)然了,實(shí)際上Array.Copy的判斷比這里寫的復(fù)雜,具體還是參考MSDN吧。
?
byte[]、sbyte[]之間的強(qiáng)制轉(zhuǎn)換和Array.Copy的問題,如果分別獨(dú)立的看,可能沒有什么問題。但如果我們合起來看,就會(huì)出現(xiàn)一些意想不到的狀況。比如說,你寫了一個(gè)函數(shù):
代碼
??????? /*
???????? * 將第index位的元素修改為value值,然后將整個(gè)數(shù)組的元素復(fù)制一遍。
???????? * 比如原來是{1,2,3},調(diào)用SomeBusiness(source, 8, 1)完成后就變成
???????? * {1,8,3,1,8,3}
???????? */
??????? static public sbyte[] SomeBusiness(sbyte[] source, sbyte value, int index)
??????? {
??????????? source[index] = value; // 你看,這個(gè)地方可以用的,所以下面一句看起來也應(yīng)該可以。
??????????? return (sbyte[])(Array)Duplicate((byte[])(Array)source);
??????? }
??????? static public byte[] Duplicate(byte[] source)
??????? {
??????????? var result = source.ToList(); // 竟然出現(xiàn)ArrayTypeMismatchException?百思不得其解。
??????????? result.AddRange(result);
??????????? return result.ToArray();
??????? }
?
尤其是SomeBusiness和Duplicate不是同一個(gè)人開發(fā)的時(shí)候。
總結(jié)
以上是生活随笔為你收集整理的byte[]、sbyte[]、int[]以及Array的故事的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 简单的鼠标移动窗体
- 下一篇: 阿拉伯数字转为罗马数字