這一課講述如何在c#的類中聲明索引,以使類能象數(shù)組一樣被訪問,這樣類的實(shí)例就能夠使用
數(shù)組訪問操作符[]來訪問類對(duì)象.
在c#中定義索引和在c++中定義操作符[]類似.對(duì)于那些封裝了數(shù)組或者使用起來有點(diǎn)象集合
的類,就可以使用索引,這樣用戶就能夠使用訪問數(shù)組的語法訪問這個(gè)類.
舉個(gè)例子,假定,你想要定義一個(gè)類,它使得文件就像一個(gè)字節(jié)數(shù)組一樣.如果文件非常大,把
整個(gè)文件都讀入內(nèi)存是不切合實(shí)際的,尤其是在你只想讀寫其中一小部分字節(jié)的時(shí)候更是如
此.這里定義了一個(gè)類filebytearray,使文件看起來就像一個(gè)數(shù)組一樣,但是實(shí)際上只有在
字節(jié)讀寫的時(shí)候才會(huì)進(jìn)行文件輸入輸出操作.
下面給出了如何定義一個(gè)索引屬性.
例子
在這個(gè)例子中,filebytearray使得對(duì)文件的訪問像字節(jié)數(shù)組一樣. reverse類把文件的字節(jié)
顛倒過來.你可以就那下面這個(gè)程序本身試驗(yàn)一下,執(zhí)行兩次就恢復(fù)原狀了.
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: }
運(yùn)行結(jié)果
用下面的文本文件測(cè)試這個(gè)程序.
// indexers/test.txt
public class hello1
{
public static void main()
{
system.console.writeline("hello, world!");
}
}
編譯并運(yùn)行程序如下:
indexer test.txt
type test.txt
將會(huì)產(chǎn)生如下的顯示:
}
}
;)"!dlrow ,olleh"(eniletirw.elosnoc.metsys
{
)(niam diov citats cilbup
{
1olleh ssalc cilbup
txt.tset/srexedni //
[代碼討論]
* 因?yàn)樗饕褂貌僮鞣鸞],所以注意在聲明的時(shí)候使用關(guān)鍵字this,而沒有名字.
* 上面的例子中,定義了一個(gè)下標(biāo)是長(zhǎng)整數(shù),返回值是字節(jié)的索引,在get中定義了代碼從一個(gè)
文件中讀取一個(gè)字節(jié),set中定義了代碼往一個(gè)文件中寫入一個(gè)字節(jié).
* 一個(gè)索引至少要有一個(gè)參數(shù).有時(shí)候還可以定義多個(gè)參數(shù),象一個(gè)多維虛擬數(shù)組一樣,但是這
種情況非常少見. 另外,盡管整型參數(shù)是最常見的,但是索引的參數(shù)可以是任何類型.標(biāo)準(zhǔn)的
字典類就提供了一個(gè)參數(shù)是object的索引.
* 盡管索引是一個(gè)非常強(qiáng)有力的特性,但是,只有在使用數(shù)組形式的訪問有確切的含義時(shí)才是合
適的. 例如下面就是一個(gè)不恰當(dāng)?shù)睦?
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.
}
}
}
仔細(xì)體會(huì)一下.
* 索引既可以被重載(overload),也可以被覆蓋(override).(以后詳細(xì)討論)
[高級(jí)話題]
如何創(chuàng)建一個(gè)"索引屬性"(indexed property)?
有的時(shí)候,一個(gè)類從不同的角度看,可能可以看成不同種類的集合. 一種叫做索引屬性的技術(shù)
就可以使這種對(duì)象得到實(shí)現(xiàn).
簡(jiǎn)單的說, 從字面上,我們可以理解,索引屬性,首先是一個(gè)屬性域,其次,它也是一個(gè)索引.舉個(gè)
例子,假設(shè)你想寫一個(gè)類document,用來封裝一段文本,目的是為了能夠方便地檢查拼寫,這樣你
可以把這段文本看成多個(gè)單詞的數(shù)組或者是多條語句的數(shù)組.最簡(jiǎn)單地,你可能想要按如下方式
寫檢查拼寫的代碼,
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.";
}
下面的代碼給出如何實(shí)現(xiàn)這樣一個(gè)類.為了實(shí)現(xiàn)索引屬性,你應(yīng)該注意到,這段代碼定義了一個(gè)
嵌套類,在嵌套類的內(nèi)部包含了對(duì)主類實(shí)例的引用.在主類中定義了只讀的域,用于訪問嵌套類
所定義的"虛擬數(shù)組",這兩個(gè)域就是索引屬性.
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;
}
}
注意: 要謹(jǐn)慎地使用這種技術(shù)!不能亂用.只有當(dāng)數(shù)組抽象具有特定的含義,而且能夠使你的代碼
更加清晰的時(shí)候,才應(yīng)該使用索引或者索引屬性.
國(guó)內(nèi)最大的酷站演示中心!