POINT
インデクサとプロパティはコードの読みやすく簡潔に書けるようにする目的で使う言語機能。
メソッドとして実装する内容をインデクサ、プロパティで書ける。
プログラムを用意する側も利用する側もシンプルにかけて、読みやすい。
ただし、メソッド内でする動作はインデクサでは添え字のように振る舞い、
プロパティはメンバ変数への読み書きをするようにする。
プロパティはメンバ変数であるかのようにメソッドを記述できる。
クラスのメンバ変数であるかのようにアクセス可能でありながら、
処理の内容を自分で記述できる機能
インデクサが配列であるかのようにアクセスできる機能
Property は変数の[ ふ.り ]をする.
POINT
内部データと Interface 型を一致させる必要はない。
Interface を変更せずに実装を変更する時に便利。
内部で保持するデータ形式と、外部に見せるデータ形式が一致しないときに、
そのギャップを埋めるのがプロパティの使い方の1つ
プロパティと+=演算子
POINT
+=演算子が有効に機能するデータ型のプロパティであれば、
プロパティに += 演算子を使うことができる。
system.get_somethig().get_hoge(index)
// 簡潔にかけて読みやすい。
system.somethig[index]
.NET 3.0 からはメンバ変数の定義の省略ができる。
set, get を記述するだけ
public int Count {
set; get;
}
WARNING
プロパティの意味に矛盾する実装はしないこと。
class Sum
{
private int sum = 0;
public int Add
{
set { sum += value; }
}
public int Result
{
get { return sum; }
}
}
Sum sum = new Sum();
// 代入ではなく足し算が実行される
sum.Add = 1;
private int sum = 0;
// Property
public int Value
{
set { sum = value; }
get { return sum; }
}
Sum sum2 = new Sum();
sum2.Value += 1;
sum2.Value += 2;
// インデクサの添え字を足し算に使用しており
// 定義を全て読まないと挙動がわからない。
public int this[int index]
{
get
{
return a + index;
}
set
{
a = index + value;
}
}
// 内部データと interface となる型を一致させる必要はなし.
private int number;
public string Order
{
get {
switch( number )
{
case 1:
return "one";
case 2:
return "two";
case 3:
return "three";
default:
return "unknown";
}
}
set {
switch( value )
{
case "one":
number = 1;
break;
case "two":
number = 2;
break;
case "three":
number = 3;
break;
default:
number = -1;
break;
}
}
}
■ read/writeを制限する
POINT
読み出し専用インデクサ
インデクサを定義する場合はgetとsetの2つの内容を記述するのが普通だが、
getだけ、あるいはsetだけを記述して読み出し専用、書き込み専用のインデクサを作れる。
読み出し専用インデクサへの書き込みはコンパイル・エラーとなる。
class Class2
{
// 宣言 + 初期化.
private char [] a = { 'A', 'B', 'C' };
// Indexer の構文: char を返す.
// Type this[ Type Param ]
// インデクサの宣言は名前には this を入れて、引数は角括弧[]でくくる。
public char this[int index]
{
// get、set、valueのキーワードを用いて記述する。
// 読み出し時の処理を記述する。
get
{
return a[index];
}
set
{
a[index] = value; // 暗黙パラメータ
}
}
}
class Class1
{
static void Main(string[] args)
{
Class2 t = new Class2();
for( int i=0; i< 3; i++ )
{
Console.WriteLine( t[i] );
}
t[0] = 'X';
t[1] = 'Y';
t[2] = 'Z';
for( int i=0; i< 3; i++ )
{
Console.WriteLine( t[i] );
}
}
}
POINT
インデクサによって配列のようにアクセスできるクラスがあるからと言って、
そのクラスが配列の持つ全機能を使えるわけではない。
class Class1
{
static void Main(string[] args)
{
string s = "Hello!";
char [] a = { 'H', 'e', 'l', 'l', 'o', '!' };
Console.WriteLine( a.GetUpperBound(0) );
//Console.WriteLine( s.GetUpperBound(0) ); // 'string' に 'GetUpperBound' の定義がありません。
Console.WriteLine( s.ToUpper() );
//Console.WriteLine( a.ToUpper() ); // 'System.Array' に 'ToUpper' の定義がありません。
// Type を取得する.
// System.String
Console.WriteLine( s.GetType().FullName );
// System.Char[] 型
Console.WriteLine( a.GetType().FullName );
}
}
POINT
インデクサの角括弧[]の内側に記述する値は整数とは限らない
インデクサを使用しているHashtableクラスでは連想配列が可能になる。
Hashtableクラスのインデクサは、読み書きする値も、添え字も、object 型として定義されている。
using System;
using System.Collections;
class Class1
{
static void Main(string[] args)
{
Hashtable h = new Hashtable();
h["斉藤"] = "Windows 2000";
h["田中"] = "Windows 98";
h["鈴木"] = "FreeBSD";
Console.WriteLine( h["斉藤"] );
Console.WriteLine( h["田中"] );
Console.WriteLine( h["鈴木"] );
}
}
[attributes] [modifiers] indexer-declarator {accessor-declarations}
DESC
インデクサを使うと配列でない要素に添字を使用できる。
使いどころとしては簡単なリストを作成し、添字を用いてアクセスしてみる。
ベクトルのように内部で配列を扱うオブジェクトは
内部の配列とオブジェクトの操作をインデクサによって通信させるという方法をとる。
こうすれば
メソッドの仕様を覚える必要なく、クラスの利用者は直感的にプログラムできる。
// こんな感じになるらしい.
T this[int id] { get ; set ; }
オブジェクトを配列のように扱う方法
この配列にアクセスする手段として、インデクサを採用するという設計をとる.
indexer-declarator
type this [formal-index-parameter-list]
インデクサの機能はプロパティの仕様に類似している。
配列のようなオブジェクト操作を暗黙的にメソッドに変換する機能がインデクサ。
インデクサを使いこなすには、一定以上のオブジェクト指向の「設計」経験が必要。
プロパティは名前で識別されるが
インデクサはシグネチャで識別される。
public class list
{
private static int item = 1;
private object value;
private list next;
public list( object o ){
value = o;
next = null;
}
// index を指定して取得
public object get( int index ){
if( index > item - 1 )
return "index error";
list l = this;
while( index != 0 ){
l = l.next;
index--;
}
return l.value;
}
// index を指定してセット
public void set( int index, object value ){
list l = this;
while( index != 0 ){
l = l.next;
index--;
}
l.value = value;
}
// Indexer
// 要は Property の [ index ] version
public object this[int index]{
// 取得する場合は
get{ return this.get(index); }
// セットする場合は,
set{ this.set( index, value ); }
}
// 要素を追加
public void add( object o ){
list l = this;
while( l.next != null ) {
l = l.next;
}
l.next = new list( o );
item++;
}
public void show(){
list l = this;
Console.WriteLine( "item : {0}", item );
do{
Console.WriteLine( l.value );
l = l.next;
}while( l != null );
}
}
{
public static void Main(){
list l = new list( "item1" );
l.add( "item2" );
l.add( 300 );
l.show();
object o = l[0];
l[0] = l[1];
l[1] = o;
l.show();
Console.WriteLine( l[2] );
Console.WriteLine( l[3] );
}
}