byte[]、sbyte[]、int[]以及Array的故事

byte[]、sbyte[]、int[]以及Array的故事

很久没有搞比较底层一点的东西了,最近又开始搞,于是乎又发现了一些很鸡毛蒜皮的事情。也许有人已经发现过了,那就请原谅我就再来挖掘一遍。

 

byte[]、sbyte[]、int[]等数组,是一种特殊的类型,他们都继承自Array。不过这个继承还不是一般的继承关系,编译器和CLI都做了一些特殊的工作。我们先看普通的继承关系:

class Human
{
}

class Man : Human
{
}

class Woman : Human
{
}

 

如果我们试图写下列的代码:

Human human = new Man();
Woman woman = (Woman)human;
 

那么结果是什么,你懂的。

 

好吧,你不懂。那我给说一下,Man和Woman都是从Human这个类继承的,因此Man和Woman是兄妹关系,兄妹之间实际上是不能互相转换的。虽然上面的代码是可以编译通过的,但是运行起来就会抛异常,告诉你Man类型的对象是不能转换成Woman的。

 

不过换到byte[]、sbyte[]等数组里面,就不是这么简单了。比如说:

byte[] source = {1, 2, 3};
sbyte[] target = (sbyte[])(Array)source;
foreach(var item in target)
{
    Console.WriteLine(item);
}

 

你猜怎么着?哎?为啥居然能跑呢?按道理byte[]和sbyte[]都是从Array派生的,那么他们之间自然也是兄妹关系了。看来CLI为我们做了一些事情。好了,那么当我们有这么一个函数:

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);
}

 

然后这么调用:

sbyte[] target = GetTarget();
sbyte[] another = new sbyte[3];
Copy(target, another);

 

在Copy函数内会出现除了ArgumentNullException之外的其它异常吗?这个怎么看也是不能吧?错了,可能会出现InvalidCastException异常,假如GetTarget的代码是:

 

sbyte[] GetTarget()
{
    return (sbyte[])(Array)new byte[] {1, 2, 3};
}
 

 

 

这就奇怪了,为啥咧?因为Array.Copy会判断要复制的数组,其元素类型之间是什么关系。这个还不是简单的相等关系,说这个之前,我们再看另一个古怪的例子:

byte[] source = { 1, 2, 3 };
int[] intArray = (int[])(Array)source;
 

这个照样还是能够编译的,不过运行时会抛出来InvalidCastException。飞鸽传书:http://www.freeeim.com/,为什么sbyte[]和byte[]之间可以互相转换,同样是兄妹关系的byte[]和int[]就不能互相转换呢?其实,这个问题不需要深究都会知道,sbyte和byte[]的元素所占字节数都一样,所以理论上是可以直接转换来访问的。而int[]和byte[]之间,如果因为其元素一个是4字节另一个是1字节,而导致不允许直接进行转换,那也很正常。不过,既然sbyte[]和byte[]之间都可以强制转换,而且转换之后都可以访问元素,为啥就不能通过Array.Copy来复制呢?嗯,不用想,Array.Copy对于源数组和目标数组的元素类型进行了判断,至于咋判断的Reflector看不到,我们就先猜测吧:Array.Copy只要发现源和目标数组的元素类型不一样,那就不能够复制。真是这样吗?我们再看另一个例子:

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);
}
 

 

怎么样,这个代码你觉得跑起来会如何?抛InvalidCastException?ArrayTypeMismatchException?其实什么都不会抛出来。原来,Array.Copy检验的条件是,如果源和目标数组的元素类型是内置的值类型,只要能做宽转换(widening conversion)那就可以成功复制。比如byte->int,反之就会抛异常。而用户自定义的值类型就没有这个优惠政策了,哪怕你重写了类型转换操作符,或者实现了IConvertible,都不行。当然了,实际上Array.Copy的判断比这里写的复杂,具体还是参考MSDN吧。

 

byte[]、sbyte[]之间的强制转换和Array.Copy的问题,如果分别独立的看,可能没有什么问题。但如果我们合起来看,就会出现一些意想不到的状况。比如说,你写了一个函数:

代码
        /*
         * 将第index位的元素修改为value值,然后将整个数组的元素复制一遍。
         * 比如原来是{1,2,3},调用SomeBusiness(source, 8, 1)完成后就变成
         * {1,8,3,1,8,3}
         */
        static public sbyte[] SomeBusiness(sbyte[] source, sbyte value, int index)
        {
            source[index] = value; // 你看,这个地方可以用的,所以下面一句看起来也应该可以。
            return (sbyte[])(Array)Duplicate((byte[])(Array)source);
        }

        static public byte[] Duplicate(byte[] source)
        {
            var result = source.ToList(); // 竟然出现ArrayTypeMismatchException?百思不得其解。
            result.AddRange(result);
            return result.ToArray();
        }

 

尤其是SomeBusiness和Duplicate不是同一个人开发的时候。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值