DOBON.NET DOBON.NETプログラミング掲示板過去ログ

[C#] 配列の確保について

環境/言語:[Windows7 C#]
分類:[.NET]

お世話になります。
最近、C#と.NETの勉強を始めた初心者です。

現在、petzold氏の「C#によるプログラミングWindows」を読んでいるのですが、
159ページに以下のようなコードがあります。

public static string[] Labels
{
get
{
return new string[]
{
"aaaaaaaaa",
"bbbbbbbbbbbb",
"ccccccccccccccccccc",
"ddddddddddddddd",

(以下、50行くらい続きます・・・)
};
}
}

これは「Label」というプロパティを参照すると、文字列の配列を返すものなの
ですが、プロパティを参照するたびに大きな配列が作成されるということは
ないのでしょうか?
それとも、2度目以降の呼び出しでは、以前の配列と同じものが使われて
いるのでしょうか?

実際にこのサンプルでは、OnPaint()からLabelsプロパティを使用しています。

デバッガで見てみても、参照型はポインタと違ってアドレスのようなものが
表示されないので、同じ配列を再利用しているのか、その都度作成しているのか
よく分かりません。

また、これに関連して、同じクラスの参照型の変数が2つある時に、
これらが同じオブジェクトを指しているのか、
内容が同一の2つのオブジェクトをそれぞれ指しているのかを
区別する方法というのはあるのでしょうか?

ご存じの方がいましたら教えて頂けると助かります。
■No27818に返信(ムーンライトさんの記事)
> これは「Label」というプロパティを参照すると、文字列の配列を返すものなの
> ですが、プロパティを参照するたびに大きな配列が作成されるということは
> ないのでしょうか?

毎回、作成されます。

> デバッガで見てみても、参照型はポインタと違ってアドレスのようなものが
> 表示されないので、同じ配列を再利用しているのか、その都度作成しているのか
> よく分かりません。

たとえば、以下のようなクラス・プロパティがあったとします。
(イメージなのでコンパイルしていません)

class Test {
private readonly double[] _body = new double[2] { 0, 0 };
public double[] Member { get { return _body; } }
}

この前提で以下のコードを実行すると result は true になります。

Test t = new Test();
double[] a = t.Member;
a[0] = 1;
double[] b = t.Member;
bool result = (b[0] == 1);

配列は参照型なので、同じものを返す場合は上記のように外部で”書き換える”ことで検証可能です。

> また、これに関連して、同じクラスの参照型の変数が2つある時に、
> これらが同じオブジェクトを指しているのか、
> 内容が同一の2つのオブジェクトをそれぞれ指しているのかを
> 区別する方法というのはあるのでしょうか?

ReferenceEquals メソッドがそれにあたります。
Azulean さま

アドバイスありがとうございました。
「ReferenceEquals」というメソッドがあったんですね。

これで確認してみたところ、確かに毎回配列が作成されていました。
つまり、petzold氏のサンプルコードはかなりメモリを無駄使いするコード
ということになりますね。

とても勉強になりました。m(_ _)m
■No27820に返信(ムーンライトさんの記事)
> これで確認してみたところ、確かに毎回配列が作成されていました。
> つまり、petzold氏のサンプルコードはかなりメモリを無駄使いするコード
> ということになりますね。

先の投稿でも少しにおわせていますが、同じ配列を毎回返すようするとメモリを節約できますが、誤動作のリスクがあります。
そもそも、配列を返すプロパティは推奨されません。

http://msdn.microsoft.com/ja-jp/library/0fss9skc.aspx

このページのタイトルがちょっとおかしいですが、配列はメモリ効率を意識しつつ、安全なプロパティを実現できないことを示唆しています。


メモリ効率を重視→外部から書き換えられる可能性があり、安全ではないプロパティになる。
安全性を重視→参照される度に配列が生成されるので、効率の悪いプロパティになる。
■No27821に返信(Azuleanさんの記事)
>> petzold氏のサンプルコードはかなりメモリを無駄使いするコード
「かなり」になるかどうかは、測定してみないと分かりませんが、
インスタンスを作り直すような実装の場合には、個人的には
プロパティではなくメソッドとして実装する方が好みです。(^^;

> http://msdn.microsoft.com/ja-jp/library/0fss9skc.aspx
> このページのタイトルがちょっとおかしいですが、
本来の訳は、「プロパティでは配列を返すべきではない」でしょうかね。
should not のはずが、cannot であるかのように訳されています。
http://msdn.microsoft.com/en/library/0fss9skc.aspx

> メモリ効率を重視→外部から書き換えられる可能性があり、安全ではないプロパティになる。
> 安全性を重視→参照される度に配列が生成されるので、効率の悪いプロパティになる。
外部変更を許可しない場合には、配列をそのまま使う代わりに
ReadOnly なコレクションのインスタンスを利用する手もあります。
戻り値は string[] ではなくなってしまいますけれども。

http://msdn.microsoft.com/ja-jp/library/53kysx7b.aspx
http://msdn.microsoft.com/ja-jp/library/t304hsay.aspx
http://msdn.microsoft.com/ja-jp/library/system.collections.readonlycollectionbase.aspx

DOBON.NET | プログラミング道 | プログラミング掲示板