.net column
.NET開発者のためのブログメディア

C#のreadonlyの使い方と注意点を解説します!

2020年06月23日

SE
C#のreadonlyの注意点には、どのようなものがあるのでしょうか?

PM
ローカル変数の宣言には使えないので注意が必要です。

C#のreadonlyとは?

C#にはreadonlyというクラスフィールド(クラスのメンバ変数)の修飾子があります。これを使いこなすことで、バグの少ない安全なプログラムをコーディングすることができます。それでは具体的な使い方を説明しましょう。以下がサンプルです。

class Human
{
public String name;
public int age;

// コンストラクタ
public Human(String n, int a)
{
name = n;
age = a;
}
}

class Program
{
static Human h;
static void Main(string[] args)
{
h = new Human(“山田太郎”, 20);
h.age = 30; // 年齢を修正
}
}

readonlyで読み取り専用にできる

上のサンプルはMainメソッド内で、nameとageというクラスフィールドを持つHumanクラスをnewで生成し、ageの値を後から変更しています。もし、このnameとageを後から変更できないようにしたい場合はどうすれば良いのでしょうか。

ここで役立つのがreadonlyです。Humanクラスを以下のように変更してください。

class Human
{
public readonly String name; // readonlyを付加
public readonly int age; // readonlyを付加

// コンストラクタ
public Human(String n, int a)
{
name = n;
age = a;
}
}

readonlyでコンストラク内のみ値が変更可能になる

上のようにクラスフィールドにreadonlyを追加することで読み取り専用になります。もし以下のようにすると、

h = new Human(“山田太郎”, 20); // これは問題ない
h.name = “山田花子”; // これはエラー
h.age = 30; // これもエラー

2、3行目は「読み取り専用フィールドを割り当てることはできません」と言うエラーになります。これで変更されたくないフィールドを保護することができます。

なお、newする時のコンストラクタ内での代入はエラーになりません。readonlyのフィールドは、newで呼ばれるコンストラクタ内だけ自由に値を変更することができます。

readonlyはクラス型フィールドにも使用できる

readonlyはクラス型のフィールドにも使用できます。以下をご覧ください。

class Human
{
public String name;
public int age;

public Human(String n, int a)
{
name = n;
age = a;
}
}

class Program
{
static readonly Human h = new Human(“山田太郎”, 20); // readonlyで宣言
static void Main(string[] args)
{
h.name = “山田花子”; // 変更可能
h.age = 30; // 変更可能
h = new Human(“佐藤一郎”, 35); // エラー
}
}

Human型のフィールドのhはreadolyのため、新たにnewするとエラーになります。ただし、hのクラスフィールドのnameやageは、hがreadonlyでも変更できます。

クラスフィールドを変更できないようにするには、各フィールドをreadonlyにするか、getのみのプロパティにする必要があります。

readonlyで出来ないこと

便利なreadonlyですが制約もあります。ローカル変数の宣言にreadonlyは使えません。メソッドの中で以下のようにするとエラーになります。

readonly int value = 100;

このような場合は、constを使用します。

const int value = 100;

これでvalueは常に100になります。なぜ、C#には似たような機能のconstとreadonlyがあるのかというと、それはconstは静的でreadonlyは動的という使い方の違いがあるからです。

別の言い方をすると、上の例でvalueはコンパイル時に100という数値に置き換えられますが、readonlyの変数は読み取り専用の変数として扱われます。

constでは出来ないこと

constにもreadonlyと比べて出来ないことがあります。まず、constの変数は宣言時に必ず値を代入して定数とする必要があります。readonlyのように後からコンストラクタ内で値を設定する、ということができません。

また、newをするクラス型の変数にもconstは使えません。ただしnewをせずnullを代入して宣言する場合はconstにできます。nullの定数を宣言しても、使い道はあまりありません。

readonlyとconstを上手く使い分けよう

上で説明したconstで出来ないことの実例は以下になります。

const int value; // 値を代入してないのでエラー
const Human const_h = new Human(“山田太郎”, 20); // newは出来ないのでエラー
const Human null_h = null; // これはOK。ただし使い道は無い

const変数は宣言時に値が確定してなければならないということです。クラス型の変数にはアドレスが入るため、newする時のメモリの状況によって値が変化してしまいます。

元々、constはC言語など古くからある定数を宣言するための機能でした。C#のreadonlyは定数ではないクラスフィールドをreadonlyにする目的で、後から登場しました。両方を上手く使い分けましょう。

readonlyは構造体の場合はクラスと動作が変わる

C#にはクラスに似た機能の構造体、structがあります。readonlyはstructの場合はクラスと動作が変わります。以下はそれを説明するためのサンプルです。

// 構造体を宣言
struct Human
{
public String name;

//コンストラクタ
public Human(string s)
{
name = s;
}

public void setName(string s)
{
name = s;
}
}

class Program
{
static readonly Human h1 = new Human(“山田太郎”); // readonlyで宣言
static void Main(string[] args)
{
h1.setName(“佐藤一郎”);
Console.WriteLine(h1.name); // 山田太郎のまま
Human h2 = h1; // 非readonlyのh2に代入
h2.setName(“鈴木花子”);
Console.WriteLine(h2.name); // 鈴木花子に変わる
}
}

構造体へのreadonlyの使い方

上のサンプルを実行すると、

山田太郎
鈴木花子

と表示されます。なぜ1つ目は佐藤一郎にならないのでしょうか。それは、h1はreadonlyのため、setNameが呼ばれた時に実体とは別のnameが複製されて、そこに”佐藤一郎”が代入されたからです。よってreadonlyの場合は構造体の変数は変更されません。

しかし、この場合ミスが起こりやすいです。そこでC#のVer. 8.0からは構造体のメソッドにreadonlyを付けられるようになりました。上の例では、

readonly public void setName(string s)
{
name = s; // エラーになる
}

とすることができます。これでname = s;がエラーになるので間違いは起こりません。nameはコンストラクタで初期化して、他のメソッドにはreadonlyを付けて変更できないようにするとよいでしょう。

参照変数のrefにもreadonlyを付けられる

C#にはrefという、他のメソッドに変数を参照で渡すキーワードがあります。C#のVer. 7.2からはこれにreadonlyを使用できます。サンプルは以下です。

static void refTest(ref int val)
{
ref readonly int r_val = ref val; // 読み取り専用の参照変数
Console.WriteLine(r_val);
val++;
}

static void Main(string[] args)
{
int a = 3;
refTest(ref a);
Console.WriteLine(a);
}

ref readonlyで分かりやすいソースコードになる

上のサンプルを実行すると、

3
4

と表示されます。refTestに渡した変数int aは、refTest内ではref int valという参照変数として扱われます。refTestではr_valという読み取り専用の参照変数にvalを代入しています。それを表示していますが、3のままです。r_valを変更しようとするとreadonlyのためエラーになります。

refTestではvalが加算されますが、これはaの参照変数なのでaが加算されます。そのためaを表示すると、1増えて4になります。

参照変数を使用するメソッド内では、ref readonlyを使うことで読み取り専用であることを明示できてわかりやすくなるでしょう。

SE
C#のreadonlyはさまざまなことができるのですね。

PM
そうですね。バグが少ないソースコードを記述できるようになるので上手に活用しましょう!

C#のreadonlyの使い方を知ろう!

C#のreadonlyについて解説しましたが、いかがでしたか。読み取り専用にしたい変数にreadonlyを使えば、間違えて値を変更することが無くなるのでバグが少ないソースコードを記述できるようになります。

安全なC#プログラミングを実現するために、ぜひreadonlyを活用してください。


.NET分野でのキャリアアップをお考えの方は、現在募集中の求人情報をご覧ください。

また、直接のエントリーも受け付けております。

エントリー(応募フォーム)

Search

Popular

reccomended

Categories

Tags