Tuesday, February 13, 2007

Indexers in C# Code Example

Defining an indexer allows you to create classes that act like "virtual arrays." Instances of that class can be accessed using the [] array access operator. Defining an indexer in C# is similar to defining operator [] in C++, but is considerably more flexible. For classes that encapsulate array- or collection-like functionality, using an indexer allows the users of that class to use the array syntax to access the class.

For example, suppose you want to define a class that makes a file appear as an array of bytes. If the file were very large, it would be impractical to read the entire file into memory, especially if you only wanted to read or change a few bytes. By defining a FileByteArray class, you could make the file appear similar to an array of bytes, but actually do file input and output when a byte was read or written.

In addition to the example below, an advanced topic on Creating an Indexed Property is discussed in this tutorial.

Example
In this example, the class FileByteArray makes it possible to access a file as if it were a byte array. The Reverse class reverses the bytes of the file. You can run this program to reverse the bytes of any text file including the program source file itself. To change the reversed file back to normal, run the program on the same file again.

// indexer.cs
// arguments: indexer.txt
using System;
using System.IO;

// Class to provide access to a large file
// as if it were a byte array.
public class FileByteArray
{
Stream stream; // Holds the underlying stream
// used to access the file.
// Create a new FileByteArray encapsulating a particular file.
public FileByteArray(string fileName)
{
stream = new FileStream(fileName, FileMode.Open);
}

// Close the stream. This should be the last thing done
// when you are finished.
public void Close()
{
stream.Close();
stream = null;
}

// Indexer to provide read/write access to the file.
public byte this[long index] // long is a 64-bit integer
{
// Read one byte at offset index and return it.
get
{
byte[] buffer = new byte[1];
stream.Seek(index, SeekOrigin.Begin);
stream.Read(buffer, 0, 1);
return buffer[0];
}
// Write one byte at offset index and return it.
set
{
byte[] buffer = new byte[1] {value};
stream.Seek(index, SeekOrigin.Begin);
stream.Write(buffer, 0, 1);
}
}

// Get the total length of the file.
public long Length
{
get
{
return stream.Seek(0, SeekOrigin.End);
}
}
}

// Demonstrate the FileByteArray class.
// Reverses the bytes in a file.
public class Reverse
{
public static void Main(String[] args)
{
// Check for arguments.
if (args.Length == 0)
{
Console.WriteLine("indexer ");
return;
}

FileByteArray file = new FileByteArray(args[0]);
long len = file.Length;

// Swap bytes in the file to reverse it.
for (long i = 0; i < len / 2; ++i)
{
byte t;

// Note that indexing the "file" variable invokes the
// indexer on the FileByteStream class, which reads
// and writes the bytes in the file.
t = file[i];
file[i] = file[len - i - 1];
file[len - i - 1] = t;
}

file.Close();
}
}
Input: indexer.txt
To test the program you can use a text file with the following contents (this file is called Test.txt in the Indexers Sample).

public class Hello1
{
public static void Main()
{
System.Console.WriteLine("Hello, World!");
}
}
To reverse the bytes of this file, compile the program and then use the command line:

indexer indexer.txt
To display the reversed file, enter the command:

Type indexer.txt
Sample Output
}
}
;)"!dlroW ,olleH"(eniLetirW.elosnoC.metsyS
{
)(niaM diov citats cilbup
{
1olleH ssalc cilbup

No comments: