C# の高度な手法 (C# と Java の比較)
更新 : 2007 年 11 月
C# は、インデクサ、属性、デリゲートなどの高度なプログラミング手法を可能にする便利な言語機能を搭載しています。
インデクサ
インデクサを使用すると、配列と同じ方法でクラスや構造体にアクセスできます。たとえば、社内の単一の部署を表すクラスを作成し、このクラスに部署の従業員全員の名前を含めると、次のようにインデクサを使用してこれらの名前にアクセスできます。
sales[0] = "Nikki";
sales[1] = "Becky";
インデクサを有効にするには、クラス定義などで、次のシグネチャを持つプロパティを定義します。
public string this [int index] //indexer
次に、通常のプロパティの場合と同じように get メソッドと set メソッドを指定します。インデクサの使用時に参照先の内部メンバを指定するのがこれらのアクセサです。
次の簡単な例では、Department というクラスを作成します。このクラスはインデクサを使用して、内部的には文字列の配列として表現される、該当する部署の従業員にアクセスします。
public class Department
{
private string name;
private const int MAX_EMPLOYEES = 10;
private string[] employees = new string[MAX_EMPLOYEES]; //employee array
public Department(string departmentName) //constructor
{
name = departmentName;
}
public string this [int index] //indexer
{
get
{
if (index >= 0 && index < MAX_EMPLOYEES)
{
return employees[index];
}
else
{
throw new System.IndexOutOfRangeException();
}
}
set
{
if (index >= 0 && index < MAX_EMPLOYEES)
{
employees[index] = value;
}
else
{
throw new System.IndexOutOfRangeException();
}
}
}
// code for the rest of the class...
}
これで、次のコード例に示すように、このクラスのインスタンスを作成し、アクセスできます。
class TestDepartment
{
static void Main()
{
Department sales = new Department("Sales");
sales[0] = "Nikki";
sales[1] = "Becky";
System.Console.WriteLine("The sales team is {0} and {1}", sales[0], sales[1]);
}
}
出力は次のとおりです。
The sales team is Nikki and Becky
詳細については、「インデクサ (C# プログラミング ガイド)」を参照してください。
属性
C# には、属性と呼ばれる、型の宣言情報を追加する機構があります。属性は、Java の注釈の概念と似ています。型に関する追加情報は、型定義の前の宣言タグの中に配置されます。以下の例は、.NET Framework 属性を使用してクラスやメソッドを装飾する方法を示しています。
次の例では、WebMethodAttribute 属性を追加することにより、GetTime メソッドを XML Web サービスとしてマークしています。
public class Utilities : System.Web.Services.WebService
{
[System.Web.Services.WebMethod] // Attribute
public string GetTime()
{
return System.DateTime.Now.ToShortTimeString();
}
}
WebMethod 属性を追加すると、.NET Framework は、この関数を呼び出すために必要な XML/SOAP 交換を自動的に処理します。この Web サービスを呼び出すと、次の値が取得されます。
<?xml version="1.0" encoding="utf-8" ?>
<string xmlns="http://tempuri.org/">7:26 PM</string>
次の例では、SerializableAttribute 属性を追加して、Employee クラスをシリアル化できるクラスとしてマークします。Salary フィールドはパブリック フィールドとしてマークされていますが、NonSerializedAttribute 属性によってマークされているため、シリアル化できません。
[System.Serializable()]
public class Employee
{
public int ID;
public string Name;
[System.NonSerialized()] public int Salary;
}
詳細については、「カスタム属性の作成 (C# プログラミング ガイド)」を参照してください。
デリゲート
C++ や Pascal などの言語は、実行時に呼び出す関数を選択できるようにする関数ポインタの概念をサポートしています。
Java には、関数ポインタの機能を持つ構成要素がありませんが、C# には用意されています。Delegate クラスを使用することによって、デリゲート インスタンスは、呼び出し可能なエンティティであるメソッドをカプセル化します。
インスタンス メソッドの場合、デリゲートは、格納しているクラスのインスタンスとそのインスタンスのメソッドで構成されます。また静的メソッドの場合、呼び出し可能なエンティティは、クラスとそのクラスの静的メソッドで構成されます。そのため、デリゲートは、任意のオブジェクトの関数を呼び出すために使用できます。デリゲートは、オブジェクト指向のタイプ セーフで安全な型です。
デリゲートを定義して使用する際には、次の 3 つのステップがあります。
宣言
インスタンス化
呼び出し
デリゲートは、次の構文を使用して宣言します。
delegate void Del1();
これで、このデリゲートを使用して、void を返し、引数を受け取らないすべての関数を参照できます。
また、文字列パラメータを受け取り、long を返す任意の関数のデリゲートを作成する場合は、次の構文を使用します。
delegate long Del2(string s);
このデリゲートは、次のようなシグネチャを含むすべてのメソッドに割り当てることができます。
Del2 d; // declare the delegate variable
d = DoWork; // set the delegate to refer to the DoWork method
ここで、DoWork のシグネチャは次のとおりです。
public static long DoWork(string name)
デリゲートの再割り当て
Delegate オブジェクトは変更不可です。つまり、対応するシグネチャは、いったん設定すると変更できなくなります。ただし、シグネチャが同じであれば、別のメソッドを指すことができます。この例では、d を新しいデリゲート オブジェクトに再割り当てし、d が DoMoreWork メソッドを呼び出すようにします。このような再割り当ては、DoWork と DoMoreWork が共に同じシグネチャを持つ場合に限られます。
Del2 d; // declare the delegate variable
d = DoWork; // set the delegate to refer to the DoWork method
d = DoMoreWork; // reassign the delegate to refer to the DoMoreWork method
デリゲートの呼び出し
デリゲートを呼び出すのは簡単です。メソッド名をデリゲート変数の名前に置き換えるだけです。この場合、11 と 22 の 2 つの値によって Add メソッドが呼び出され、変数 sum に割り当てられた long 型の結果が返されます。
Del operation; // declare the delegate variable
operation = Add; // set the delegate to refer to the Add method
long sum = operation(11, 22); // invoke the delegate
デリゲートの作成、インスタンス化、および呼び出しの例を次に示します。
public class MathClass
{
public static long Add(int i, int j) // static
{
return (i + j);
}
public static long Multiply (int i, int j) // static
{
return (i * j);
}
}
class TestMathClass
{
delegate long Del(int i, int j); // declare the delegate type
static void Main()
{
Del operation; // declare the delegate variable
operation = MathClass.Add; // set the delegate to refer to the Add method
long sum = operation(11, 22); // use the delegate to call the Add method
operation = MathClass.Multiply; // change the delegate to refer to the Multiply method
long product = operation(30, 40); // use the delegate to call the Multiply method
System.Console.WriteLine("11 + 22 = " + sum);
System.Console.WriteLine("30 * 40 = " + product);
}
}
出力
11 + 22 = 33
30 * 40 = 1200
デリゲート インスタンスには、オブジェクト参照を含める必要があります。上の例では、メソッドを静的メソッドとして宣言しているため、オブジェクト参照を指定する必要がありませんが、デリゲートがインスタンス メソッドを参照する場合は、次のようにオブジェクト参照を指定する必要があります。
Del operation; // declare the delegate variable
MathClass m1 = new MathClass(); // declare the MathClass instance
operation = m1.Add; // set the delegate to refer to the Add method
この例では、Add および Multiply は MathClass のインスタンス メソッドです。MathClass のメソッドが静的として宣言されていない場合は、次のように、MathClass のインスタンスを使用してデリゲートでメソッドを呼び出します。
public class MathClass
{
public long Add(int i, int j) // not static
{
return (i + j);
}
public long Multiply (int i, int j) // not static
{
return (i * j);
}
}
class TestMathClass
{
delegate long Del(int i, int j); // declare the delegate type
static void Main()
{
Del operation; // declare the delegate variable
MathClass m1 = new MathClass(); // declare the MathClass instance
operation = m1.Add; // set the delegate to refer to the Add method
long sum = operation(11, 22); // use the delegate to call the Add method
operation = m1.Multiply; // change the delegate to refer to the Multiply method
long product = operation(30, 40); // use the delegate to call the Multiply method
System.Console.WriteLine("11 + 22 = " + sum);
System.Console.WriteLine("30 * 40 = " + product);
}
}
出力
この例では、メソッドを静的メソッドとして宣言した前の例と同じ出力が得られます。
11 + 22 = 33
30 * 40 = 1200
デリゲートとイベント
.NET Framework では、Windows アプリケーションや Web アプリケーションでのボタン クリック イベントなどのイベント処理タスクでもデリゲートを頻繁に使用します。Java でのイベント処理は、通常、カスタム リスナ クラスを実装して行いますが、C# では、イベント処理にデリゲートを利用できます。イベントは、イベント宣言の前にキーワード イベントがある場合を除き、デリゲート型を使用してフィールドのように宣言します。イベントは、通常、パブリックとして宣言しますが、アクセシビリティ修飾子を使用することもできます。delegate と event の宣言の例を次に示します。
// Declare the delegate type:
public delegate void CustomEventHandler(object sender, System.EventArgs e);
// Declare the event variable using the delegate type:
public event CustomEventHandler CustomEvent;
イベント デリゲートはマルチキャストなので、複数のイベント処理メソッドへの参照を保持できます。デリゲートは、イベントに対して登録されているイベント ハンドラのリストを管理することで、そのイベントを発生させるクラスのイベント ディスパッチャとして動作します。イベントに複数の関数をサブスクライブする方法を次の例に示します。EventClass クラスには、デリゲート、イベント、およびイベントを呼び出すメソッドが含まれます。イベントの呼び出しは、イベントを宣言したクラスの中からしか行うことができないことに注意してください。TestEvents クラスは、+= 演算子を使用してイベントにサブスクライブでき、-= 演算子を使用してアンサブスクライブできます。次の例に示すように、InvokeEvent メソッドを呼び出すと、イベントが発生し、それに同期して、イベントにサブスクライブしているすべての関数が起動します。
public class EventClass
{
// Declare the delegate type:
public delegate void CustomEventHandler(object sender, System.EventArgs e);
// Declare the event variable using the delegate type:
public event CustomEventHandler CustomEvent;
public void InvokeEvent()
{
// Invoke the event from within the class that declared the event:
CustomEvent(this, System.EventArgs.Empty);
}
}
class TestEvents
{
private static void CodeToRun(object sender, System.EventArgs e)
{
System.Console.WriteLine("CodeToRun is executing");
}
private static void MoreCodeToRun(object sender, System.EventArgs e)
{
System.Console.WriteLine("MoreCodeToRun is executing");
}
static void Main()
{
EventClass ec = new EventClass();
ec.CustomEvent += new EventClass.CustomEventHandler(CodeToRun);
ec.CustomEvent += new EventClass.CustomEventHandler(MoreCodeToRun);
System.Console.WriteLine("First Invocation:");
ec.InvokeEvent();
ec.CustomEvent -= new EventClass.CustomEventHandler(MoreCodeToRun);
System.Console.WriteLine("\nSecond Invocation:");
ec.InvokeEvent();
}
}
出力
First Invocation:
CodeToRun is executing
MoreCodeToRun is executing
Second Invocation:
CodeToRun is executing