繁体中文
设为首页
加入收藏
当前位置:.Net技术首页 >> Asp.Net开发 >> 并不决定INDEXER好用(内付INDEXER的教程 中文版) :)

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

2007-06-15 08:00:00  作者:  来源:互联网  浏览次数:0  文字大小:【】【】【
简介:这一课讲述如何在C#的类中声明索引,以使类能象数组一样被访问,这样类的实例就能够使用 数组访问操作符[]来访问类对象. 在C#中定义索引和在C++中定义操作符[]类似.对于那些封装了数组或者使用起来有点象集合 的类...

这一课讲述如何在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;

}

}

注意: 要谨慎地使用这种技术!不能乱用.只有当数组抽象具有特定的含义,而且能够使你的代码

更加清晰的时候,才应该使用索引或者索引属性.

责任编辑:admin
相关文章