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

C#教学第17讲索引器3(学习笔记)

2009-01-21 21:48 295 查看
第17讲索引器3
快乐乔巴的博客

视频讲师:陈广老师
大家好,今天我们接着上节课的内容继续讲解索引器。以前呢,使用过Windows API编程的人因该记得API函数里使用了大量的位标志。位标志的特点就是在添加和删除某个选线时使用位操作,与运算或者或运算。当然,在C#中依然沿用了win32 API的位标志。C#中对位标志的操作是存放在System.Collections.BitArray命名空间中的,BitArray类在C#语言规范中公布了BitArray类的一小段的源代码。

C#语言规范是你在安装完Visual Studio 2005之后自动安装上去的,它存在于.../Microsoft Visual Studio 8/VC#/Snippets/2052文件夹底下,可以到里面找一下。

BitArray类是一个索引器,通过这个索引器我们可以对位进行操作。下面我们就来分析一下BitArray类。

BitArray类顾名思义就是位数组的意思,我们知道一个Int32占用4个Byte的空间,而一个字节等于8个bit,bit就是位。一个Int32=4×8bit=32bit。BitArray类可以存放任意长度的位,并对它进行操作。而一个整数只能放32个bit。

BitArray类中,位的存放是使用Int数组Int[] bits;就是整数类型数组。在C#中Int等于Int32都是一样的,都是一个32位的整数。换句话说,如果你的位标志小于32个元素,这时候只需要一个整数就可以存放了,那么这个整数数组就只有一个元素,如果你的位标志长度有100,这时候需要用4个整数来存放,以此类推。

在C#中最小的存储单位是字节,所以你不能直接使用bit来存放这个位,也就是说没有直接的BitArray,你只能通过索引器来实现这个位的数组。下面我们就来写这个BitArray。

我们新建一个复合工程Windows Application,然后选择Windows应用程序,然后起个名字Indexer,可以将其路径改为我们C# Demo文件夹下。建立之后我们在其工程下新建一个类BitArray,我们开始写BitArray类:

using System;
using System.Collections.Generic;
using System.Text;

namespace Indexer
{
    public class BitArray
    {
        //声明一个int数组,用它来存放位标志
        int[] bits;
        //位标志的长度用一个length来表示
        int length;
        //bits还没有确定长度,它的长度是根据length来确定的
        //我们通过构造函数来完成
        public BitArray(int length)
        {
            if (length < 0)
            { 
                //抛出一个参数无效的异常,可以用MSDN去查阅
                throw new ArgumentException();
            }

            //这段代码可能大家会看不懂,因为它涉及到位操作。
            //这时候我们可以想想,如果使用运算,怎么计算出这个长度呢。
            //在这个整数数组里,一个整数可以存32个位,那么这个数组长度就是(length-1)/32+1
            //比如如说位标识长度为33,那么上面的式子得出2,也就是说存放2个整数的长度,这是正确的
            //为什么length-1,因为当长度为32时,应该只能存放一个整数长度,而不是2。
            bit = new int[((length - 1) >> 5) + 1];
            //(length - 1) >> 5) + 1公式和(length-1)/32+1公式只是稍微有点差别,>>位移操作符
            //十进制:1  二进制:00000000000000000000000000000001
            //将00000000000000000000000000000001左移5位就是00000000000000000000000000100000
            //00000000000000000000000000100000转换成十进制正好是32,再左移一位就是64,128,256...
            //以此类推每左移一位就是把原来的数乘以2。右移一位就是除以2
            //>> 5右移5位就是除以2的5次方,也就是除以32
            //这里为什么不用/32呢,因为位移运算符的操作速度会快得很多
            //但是大家要记住我们在实际的工程中千万不要在业务逻辑运算中使用位移运算,这样会使得程序难以维护
            //在位运算中使用是没有问题的

            //给长度赋值
            this.length = length;
        }

        //下面实现索引器
        //索引器的返回值是bool,参数是整数index表示你要运算的第几位
        public bool this[int index]
        {
            get
            { 
                //判断index是否合法
                if (index < 0 || index >= length)
                { 
                    //这个字面上意思就是索引超出范围的异常
                    throw new IndexOutOfRangeException();
                }
                //返回指定位置上的比特值,就是true和false
                return (bits[index >> 5] & 1 << index) != 0;
                //bits[index >> 5]这个指示了你要读取的位在第几个数组的元素上
                //<<左移操作符的优先级要比&操作符高,所以先执行1<<index
                //也许有疑问,如果这个index是100,那么1左移100不成0了么?因为它的位最长只有32位,往左移100只剩0了
                //我们来看一下MSDN是如何说的。
                //#-- 左移运算符:将第一个操作数向左移动第二个操作数指定的位数。第二个操作数的类型必须是int。 --#
                //1 << index中1就是第一个操作数,index就是第二个操作数,关键就是下面一段话
                //#-- 如果第一个操作数是int或者uint(32位数),则移位数由第二个操作数的低5位给出 --#
                //#-- 如果第一个操作数是int或者uint(64位数),则移位数由第二个操作数的低6位给出 --#
                //如果是index是100,它的二进制就是00000000000000000000000001100100,低5位就是00100这5位,只用这5位来做位移运算
                //那么就变成00000000000000000000000000000100,转成十进制就是4
                //100%32 = 4,如果是101那么就是101%32 = 5。
                //1 << index这句话就等于1<<(index%32)

                //举个例子,假设bits的length是120,它需要4个整数来存放这些位
                //bits[0]=00011001010111000011010101111101
                //bits[1]=00011001010101001011010101110101
                //bits[2]=00011001010111001010010101110100
                //bits[3]=00011001010100001111110100110101
                //如果index是100,那么bits[index >> 5]就是bits[100 >> 5]也就是在bits[3]中的元素
                //1 << index也就是 1<<4,也就是1向左移4位那就等于10000,然后这个2个值进行&与运算
                //           00011001010100001111110100110101
                //&(与运算)  00000000000000000000000000010000
                //--------------------------------------------
                //结果       00000000000000000000000000010000
                //&运算规则是如果2个数都为1那返回就是1,如果2个数其中有任何一个不为1那就返回0。
                //(bits[index >> 5] & 1 << index) != 0 通过运算,我们就可以得到 指定位置比特的状态了
            }

            //set访问器
            set
            {
                //判断index范围是否合法
                if (index < 0 || index >= length)
                {
                    throw new IndexOutOfRangeException();
                }
                if (value)
                {
                    bits[index >> 5] |= 1 << index;   //这句话相当于bits[index>>5] = bits[index>>5] | 1 << index;
                    //假如index是100,按照上面的例子就是b[3]中的元素,然后进行|(或运算)
                    //           00011001010100001111110100110101
                    //|(或运算)  00000000000000000000000000010000
                    //--------------------------------------------
                    //结果       00011001010100001111110100110101
                    //|运算规则是只要2个数有一个为1那它的结果就为1,只有当2个数都为0的时候才为0。
                    //通过或运算,它只会对第5位产生影响(现在第5位为1,1或1所以是1,如果是0那0或1则结果会变为0)
                    //所以这个运算的实质就是给指定的位赋1,也就是把它的值变为1
                }
                else
                {
                    //这一句是其变为0
                    bits[index >> 5] &= ~(1 << index);  //这一句相当于bits[index >> 5] = bits[index >> 5] & ~(1 << index);
                    //首先我们来看看~符号是什么意思,查阅MSDN
                    //#-- ~运算符对操作数执行按位求补运算,其效相当于反转每一位。 --#
                    //可以打开windows自带的计算器,点击科学型。~求补操作符就是Not按钮操作。
                    //                                                                      1100100
                    //-----------------------------------------------------------------------------
                    //Not求补结果  1111111111111111111111111111111111111111111111111111111110011011
                    //结果表示求补就是把0变成了1,把1变成了0。
                    //~(1 << index) 就是~10000 也就等于11111111111111111111111111101111,好,下面进行&(与运算)
                    //           00011001010100001111110100110101
                    //&(与运算)  11111111111111111111111111101111
                    //--------------------------------------------
                    //结果       00011001010100001111110100100101
                    //我们仔细想想只有第5位不管0或者是1,和0作与运算结果肯定是0,其他位都不会有任何改变。
                    //所以bits[index >> 5] &= ~(1 << index);这一句实现的功能就是给指定位置赋0。
                }
            }
        }
    }
}




由于时间关系没有办法将这个例子讲完,我们留到下节课再讲,这节就讲到这里。



由快乐乔巴听课摘写笔记
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: