主页 > 编程资料 > C# >
发布时间:2015-09-26 作者:网络 阅读:196次
这一课讲述如何在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;
    }
}

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



关键字词: