首页 > 开发 > 综合 > 正文

并不决定INDEXER好用(内付INDEXER的教程 中文版) :)

2024-07-21 02:22:23
字体:
来源:转载
供稿:网友
这一课讲述如何在c#的类中声明索引,以使类能象数组一样被访问,这样类的实例就能够使用
数组访问操作符[]来访问类对象.
在c#中定义索引和在c++中定义操作符[]类似.对于那些封装了数组或者使用起来有点象集合
的类,就可以使用索引,这样用户就能够使用访问数组的语法访问这个类.
举个例子,假定,你想要定义一个类,它使得文件就像一个字节数组一样.如果文件非常大,把
整个文件都读入内存是不切合实际的,尤其是在你只想读写其中一小部分字节的时候更是如
此.这里定义了一个类filebytearray,使文件看起来就像一个数组一样,但是实际上只有在
字节读写的时候才会进行文件输入输出操作.

下面给出了如何定义一个索引属性.

例子

在这个例子中,filebytearray使得对文件的访问像字节数组一样. reverse类把文件的字节
颠倒过来.你可以就那下面这个程序本身试验一下,执行两次就恢复原状了.

000: // indexers/indexer.cs
001: using system;
002: using system.io;
003:
004: // class to provide access to a large file
005: // as if it were a byte array.
006: public class filebytearray
007: {
008:     stream stream;      // holds the underlying stream
009:                         // used to access the file.
010: // create a new filebytearray encapsulating a particular file.
011:     public filebytearray(string filename)
012:     {
013:         stream = new filestream(filename, filemode.open);
014:     }
015:
016:     // close the stream. this should be the last thing done
017:     // when you are finished.
018:     public void close()
019:     {
020:         stream.close();
021:         stream = null;
022:     }
023:
024:     // indexer to provide read/write access to the file.
025:     public byte this[long index]   // long is a 64-bit integer
026:     {
027:         // read one byte at offset index and return it.
028:         get
029:         {
030:             byte[] buffer = new byte[1];
031:             stream.seek(index, seekorigin.begin);
032:             stream.read(buffer, 0, 1);
033:             return buffer[0];
034:         }
035:         // write one byte at offset index and return it.
036:         set
037:         {
038:             byte[] buffer = new byte[1] {value};
039:             stream.seek(index, seekorigin.begin);
040:             stream.write(buffer, 0, 1);
041:         }
042:     }
043:
044:     // get the total length of the file.
045:     public long length
046:     {
047:         get {
048:             return stream.seek(0, seekorigin.end);
049:         }
050:     }
051: }
052:
053: // demonstrate the filebytearray class.
054: // reverses the bytes in a file.
055: public class reverse
056: {
057:     public static void main(string[] args)
058:     {
059:         // check for arguments.
060:         if (args.length == 0)
061:         {
062:             console.writeline("indexer ");
063:             return;
064:         }
065:
066:         filebytearray file = new filebytearray(args[0]);
067:         long len = file.length;
068:         
069:         // swap bytes in the file to reverse it.
070:         for (long i = 0; i < len / 2; ++i)
071:         {
072:             byte t;
073:
074:             // note that indexing the "file" variable invokes the
075:             // indexer on the filebytestream class, which reads
076:             // and writes the bytes in the file.
077:             t = file[i];
078:             file[i] = file[len - i - 1];
079:             file[len - i - 1] = t;
080:         }
081:
082:         file.close();
083:     }
084: }

运行结果

用下面的文本文件测试这个程序.

// indexers/test.txt
public class hello1
{
    public static void main()
    {
        system.console.writeline("hello, world!");
    }
}

编译并运行程序如下:

indexer test.txt
type test.txt

将会产生如下的显示:

}
}
;)"!dlrow ,olleh"(eniletirw.elosnoc.metsys
{
)(niam diov citats cilbup
{
1olleh ssalc cilbup
txt.tset/srexedni //

[代码讨论]

* 因为索引使用操作符[],所以注意在声明的时候使用关键字this,而没有名字.
* 上面的例子中,定义了一个下标是长整数,返回值是字节的索引,在get中定义了代码从一个
文件中读取一个字节,set中定义了代码往一个文件中写入一个字节.
* 一个索引至少要有一个参数.有时候还可以定义多个参数,象一个多维虚拟数组一样,但是这
种情况非常少见. 另外,尽管整型参数是最常见的,但是索引的参数可以是任何类型.标准的
字典类就提供了一个参数是object的索引.
* 尽管索引是一个非常强有力的特性,但是,只有在使用数组形式的访问有确切的含义时才是合
适的. 例如下面就是一个不恰当的例子.

class employee
{
    // very bad style: using an indexer to access
    // the salary of an employee.
    public double this[int year]
   {
        get
        {
            // return employee's salary for a given year.
        }
   }
}

仔细体会一下.

* 索引既可以被重载(overload),也可以被覆盖(override).(以后详细讨论)

[高级话题]
如何创建一个"索引属性"(indexed property)?

有的时候,一个类从不同的角度看,可能可以看成不同种类的集合. 一种叫做索引属性的技术
就可以使这种对象得到实现.
简单的说, 从字面上,我们可以理解,索引属性,首先是一个属性域,其次,它也是一个索引.举个
例子,假设你想写一个类document,用来封装一段文本,目的是为了能够方便地检查拼写,这样你
可以把这段文本看成多个单词的数组或者是多条语句的数组.最简单地,你可能想要按如下方式
写检查拼写的代码,

document d = new document();
// ...
for (int i = 0; i < d.words.count; ++i)
{
    if (d.words[i] == "peter")
        d.words[i] = "joe";
}
for (int i = 0; i < d.sentences.count; ++i)
{
    if (d.sentences[i] == "elvis is the king.")
        d.sentences[i] = "eric clapton is a guitar god.";
}

下面的代码给出如何实现这样一个类.为了实现索引属性,你应该注意到,这段代码定义了一个
嵌套类,在嵌套类的内部包含了对主类实例的引用.在主类中定义了只读的域,用于访问嵌套类
所定义的"虚拟数组",这两个域就是索引属性.

public class document
{
    public struct wordcollection
    {
        readonly document document;  // the containing document

        internal wordcollection(document d)
        {
             document = d;
         }

        public string this[int indexer]
        {
            get
            {
                return document.getnthword(indexer);
            }
            set
            {
                document.setnthword(indexer, value);
            }
        }
        public int count
        {
            get
            {
                return document.countwords();
            }
        }
    }

    public struct sentencecollection
    {
        readonly document document;  // the containing document

        internal sentencecollection(document d)
        {
            document = d;
        }

        public string this[int indexer] {
            get
            {
                return document.getnthsentence(indexer);
            }
            set
            {
                document.setnthsentence(indexer, value);
            }
        }
        public int count
        {
            get
            {
                return document.countsentences();
            }
        }
    }

    // because the types of the fields have indexers,
    // these fields appear as "indexed properties"
    public readonly wordcollection words;
    public readonly sentencecollection sentences;

    public document()
    {
        words = new wordcollection(this);
        sentences = new sentencecollection(this);
    }

    private string getnthword(int index)
    {
         /* ... */
         return "";
    }
    private void setnthword(int index, string w)
    {
         /* ... */
    }
    private int countwords()
    {
         /* ... */
         return 0;
    }
    private string getnthsentence(int index)
    {
         /* ... */
         return "";
    }

    private void setnthsentence(int index, string s)
    {
         /* ... */
    }
    private int countsentences()
    {
         /* ... */
         return 0;
    }
}

注意: 要谨慎地使用这种技术!不能乱用.只有当数组抽象具有特定的含义,而且能够使你的代码
更加清晰的时候,才应该使用索引或者索引属性.


国内最大的酷站演示中心!
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表