C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

VB.NET における配列のコピー

[トピック内 9 記事 (1 - 9 表示)]  << 0 >>

■83019 / inTopicNo.1)  VB.NET における配列のコピー
  
□投稿者/ ぼんかば (1回)-(2017/02/27(Mon) 13:37:31)

分類:[.NET 全般] 


VBAでは

Dim aaa(5) as single

bbb = aaa

とすると、配列のコピーを行うことができ、
bbbを変更してもaaaは変更されませんでした。

VB.NETでは


このコードでは、bbbを変更するとaaaも一緒に変更されてしまいます。


連動して変更されないようにするには、
bbb = aaa.clone

とする必要があります。

ここで疑問に思ったのですが、
このように配列のクローンを作成せず
アドレスのみをコピーするのは一体どういう時に使えば良いのでしょうか?

bbbを生成せずにそのまま、aaaを使わないで書くメリットは
どういう時にあるのでしょうか?

aaaがクラスになっており、
ccc.ddd.eee.fff
のように、長くなってしまう場合に
見やすくする時に使うためのものなのでしょうか?





引用返信 編集キー/
■83020 / inTopicNo.2)  Re[1]: VB.NET における配列のコピー
□投稿者/ WebSurfer (1152回)-(2017/02/27(Mon) 13:49:44)
No83019 (ぼんかば さん) に返信

値型と参照型というのがあることはご存知ですか?

ご存じない or 理解が曖昧という場合は以下の記事の図が分かりやすいと思いま
すので、まずそれを見てください。その後の方が話が通じやすいと思いますので。

2-3 値型と参照型
https://msdn.microsoft.com/ja-jp/library/cc406735.aspx

値型と参照型の区別と違い クラスと構造体の違い
http://dobon.net/vb/dotnet/beginner/valuereference.html
引用返信 編集キー/
■83021 / inTopicNo.3)  Re[1]: VB.NET における配列のコピー
□投稿者/ WebSurfer (1153回)-(2017/02/27(Mon) 13:55:17)
No83019 (ぼんかば さん) に返信

【追伸】

注: Single は値型ですが、その配列は参照型になります。
引用返信 編集キー/
■83022 / inTopicNo.4)  Re[2]: VB.NET における配列のコピー
□投稿者/ ぼんかば (3回)-(2017/02/27(Mon) 14:20:06)
値型と参照型の違いは
functionやsubで
byvalやbyrefといった使われ方がするので理解しています。
このように、異なるsubで二つの使い方があるのは分かるのですが
同じsubの中で参照型を使うメリットは何なのでしょうか?

引用返信 編集キー/
■83023 / inTopicNo.5)  Re[3]: VB.NET における配列のコピー
□投稿者/ shu (966回)-(2017/02/27(Mon) 14:42:14)
No83022 (ぼんかば さん) に返信
> 値型と参照型の違いは
> functionやsubで
> byvalやbyrefといった使われ方がするので理解しています。
ByVal, ByRefは関数呼び出し時に値として渡すか参照として渡すかの違いで
値型、参照型とは違います。
値型はその中身自体が値である型
参照型はその中身に値の場所がある型
変数として考えた場合、値型は中身が値なので代入すると同じ値がコピーされて代入されます
参照型は代入すると参照がコピーされて代入されます。
代入された値型の値は元と違う場所に保存されているので書き換えても元に影響が出ません。
参照型の場合は参照先は元と同じなのでそのメンバを書き換えると一緒に書き換わります。


> アドレスのみをコピーするのは一体どういう時に使えば良いのでしょうか?
配列の配列など参照する配列を切り替える必要があるようなときに使うとよいと思います。


引用返信 編集キー/
■83026 / inTopicNo.6)  Re[3]: VB.NET における配列のコピー
□投稿者/ WebSurfer (1154回)-(2017/02/27(Mon) 17:47:21)
No83022 (ぼんかば さん) に返信

> 値型と参照型の違いは
> functionやsubで
> byvalやbyrefといった使われ方がするので理解しています。

理解されてはいないように思うのですが・・・

値型・参照型とVB.NET のメソッドでの引数の渡し方(ByVal, ByRef)とは関係ないです。

クラスや配列はあくまで参照型、構造体はあくまで値型で、ByVal, ByRef どちらで渡そうと、
参照型が値型に変わったり、値型が参照型に変わることはありません。

別の入れ物に中身をコピーして渡すか(ByVal)、入れ物を中身ごと渡すか(ByRef)の違い
です。入れ物自体の型は変わりません。詳しくは以下の記事が参考になると思いますので
読んでください。

値渡しと参照渡しの違いと使い分け
http://dobon.net/vb/dotnet/beginner/byvalbyref.html

> このように、異なるsubで二つの使い方があるのは分かるのですが
> 同じsubの中で参照型を使うメリットは何なのでしょうか?

質問の意味が分かりません。

たぶん、値型・参照型と ByVal・ByRef を質問者さんが混同されていて、話が噛み合わない
のではないかと思います。

先に紹介した値型・参照型に関する記事および上に紹介した ByVal・ByRef に関する記事を
よく読んで理解してください。
引用返信 編集キー/
■83028 / inTopicNo.7)  Re[1]: VB.NET における配列のコピー
□投稿者/ furu (86回)-(2017/02/27(Mon) 19:37:21)
2017/02/27(Mon) 19:39:31 編集(投稿者)
2017/02/27(Mon) 19:38:35 編集(投稿者)
2017/02/27(Mon) 19:38:28 編集(投稿者)

■No83019 (ぼんかば さん) に返信
> ここで疑問に思ったのですが、
> このように配列のクローンを作成せず
> アドレスのみをコピーするのは一体どういう時に使えば良いのでしょうか?
> 
> bbbを生成せずにそのまま、aaaを使わないで書くメリットは
> どういう時にあるのでしょうか?

ぼんかばさんが書いてるように見やすくする時も
よく使っています。

    textBox1.Text        = "aaa";
    textBox1.BackColor   = Color.Black;
    textBox1.ForeColor   = Color.Red;
    textBox1.ReadOnly    = true;
        ↓↓↓
    t = textBox1;
    t.Text        = "aaa";
    t.BackColor   = Color.Black;
    t.ForeColor   = Color.Red;
    t.ReadOnly    = true;

でもロジックを書くときが一番使うような気がします。

    if (今日は日曜日)
        textBox1.Show();
    else if (今日は土曜日)
        textBox2.Show();
    else
        textBox3.Show();
        ↓↓↓
    t = textBox3;
    if (今日は日曜日) t = textBox1;
    if (今日は土曜日) t = textBox2;
    t.Show();

すみまんせん。VB.NET知らないのでC#で書きましたが
気持ちは伝わると思います。

引用返信 編集キー/
■83029 / inTopicNo.8)  Re[1]: VB.NET における配列のコピー
□投稿者/ Jitta (275回)-(2017/02/27(Mon) 20:21:44)
No83019 (ぼんかば さん) に返信
>
> VBAでは
>
> Dim aaa(5) as single
>
> bbb = aaa
>
> とすると、配列のコピーを行うことができ、
>
> VB.NETでは
>
>
> このコードでは、bbbを変更するとaaaも一緒に変更されてしまいます。

その前に、このコードで宣言できなかったと思いますが?
# VBとC#で違う?


> ここで疑問に思ったのですが、
> このように配列のクローンを作成せず
> アドレスのみをコピーするのは一体どういう時に使えば良いのでしょうか?

これは新鮮!メインがC/C++な私からしたら、
アドレスのみコピーの方が当たり前なので、
配列のクローンを作成する意図ってなんでしょう?
と聞いてみる。つまり、そういう風に考えた事がなかった。
引用返信 編集キー/
■83030 / inTopicNo.9)  Re[3]: VB.NET における配列のコピー
□投稿者/ 魔界の仮面弁士 (1149回)-(2017/02/27(Mon) 20:32:06)
2017/02/27(Mon) 21:46:41 編集(投稿者)

No83019 (ぼんかば さん) に返信
> VBAでは
> Dim aaa(5) as single
> bbb = aaa

それができるのは、VBA6 以降に限られますね。


VBA5 以前では
 ReDim bbb(5)
 For n = 0 To 5
  If IsObject(aaa(n)) Then
   Set bbb(n) = aaa(n)
  Else
   Let bbb(n) = aaa(n)
  End If
 Next
のように処理する必要があったと思います。(今回、If は冗長ですが)

で、上記で Set ステートメントと Let ステートメントを明示的に記述していますが、
VBA の場合、参照情報のコピーには Set ステートメントを使います。しかしながら
VBA の配列はオブジェクトではないため、「Set bbb = aaa」のようには書けず、
参照のコピーにはなりません。

逆に、VB.NET の配列は常に参照型なので、常に参照のコピーとなります。



> 連動して変更されないようにするには、
> bbb = aaa.clone
> とする必要があります。

Linq が使えるバージョンであれば、
 bbb = aaa.ToArray()
とする手もありますよ。Clone だと戻り値が Object 型になりますが、
これなら元の型を維持したデータ型で受け取れます。
(Clone と ToArray の動作は同一では無いですが)


ただ、どちらの方法にしても、「連動して変更されない」ことが保証されるのは
コピー元の変数が値型(構造体)の配列だった場合だけです。

もしも値体の配列ではなく、参照型の配列だった場合は、
やはり連動して変更されてしまう可能性があります。


 Dim x(0) As System.Net.IPAddress
 Dim y() As System.Net.IPAddress

 x(0) = New System.Net.IPAddress(10)

 '配列変数の単純な代入
 y = x
 '下記は True となる(x と y は同じ参照)
 Console.WriteLine(Object.ReferenceEquals(x, y))


 '配列の Clone を代入
 y = DirectCast(x.Clone(), System.Net.IPAddress())

 '下記は False となる(x と y は別の参照)
 Console.WriteLine(Object.ReferenceEquals(x, y))
 '下記は True となる(各要素の参照がコピーされた)
 Console.WriteLine(Object.ReferenceEquals(x(0), y(0)))

 'Clone だとしても、連動して変更される
 Console.WriteLine(y(0).Address) '10
 x(0).Address = 20
 Console.WriteLine(y(0).Address) '20



> aaaがクラスになっており、
> ccc.ddd.eee.fff
> のように、長くなってしまう場合に
> 見やすくする時に使うためのものなのでしょうか?

そうですね。見やすくするだけでなく、メンバー参照の回数も減るので、
何度も利用する場合は、僅かに高速化にもなるかもしれません。



No83022 (ぼんかば さん) に返信
> 同じsubの中で参照型を使うメリットは何なのでしょうか?

『参照型を使うメリット』という問いの意図が読めませんでした。


質問内容は「VB.NET における配列のコピー」についてでしたよね。
配列に限らず、コレクションはそもそも参照型として実装されていますので、
複数の値をまとめて取り扱うのであれば、そもそも参照型を使う以外の選択肢がありません。

配列の代わりに IntPtr や C# の unsafe コードを使うということはできますが、
本質的には変わらないでしょう。それに、比較対象とした VBA であっても、
内部的には SafeArray API 経由での読み書きになっているわけで。


それとも、「Dim x() As 値型」と「Dim y() As 参照型」のどちらを使うべきか、という話でしょうか?
あるいは、配列が良いか List(Of )等が良いか、という話でしょうか?



> 値型と参照型の違いは
> functionやsubで
> byvalやbyrefといった使われ方がするので理解しています。

値型と参照型、値渡しと参照渡しはそれぞれ別物ですが、
考え方は似たような物ですし、そこは理解頂いているものとして:


ざっくり言えば、
 VBA の場合:IsObject が True を返す変数なら参照型
 VB.NET の場合:Class または Interface な変数なら参照型
と言えそうです。まぁ、細かく分類していくともう少し複雑ですが、
配列を別の配列に代入する行為は、VBA であれ VB.NET であれ、基本的に shallow copy です。


しかし、VB.NET の配列は参照型なのに、VBA の方はそうではないため、この点が差異として現れます。

VB.NET の配列は、実体としては System.Array クラスのインスタンスなので、
Single 型が値型であったとしても「Single の配列」は参照型です。
そして VB.NET での配列の代入操作では、「配列そのもの」の参照がコピーされることになります。

一方 VBA の場合、配列そのものは参照型ではないので(実際には SAFEARRAY のポインタですが)、
Set bbb = aaa のような操作はできず、Let 相当の操作となります。そして Let 操作であるが故、
配列そのもののコピーではなく、別の配列が確保され、そこに各要素がコピーされる仕様です。

配列の各要素が参照型で無いプリミティブ型だった場合は、データ内容が複写されますが、
配列の各要素が参照型であった場合は、その参照情報が複製されることになります。

一応、VBA であっても、一つの配列インタンスが複数の変数から同時に
参照される場面はありますが、その場合、一方の変数で配列を操作している
最中に、他方の変数から同一の配列を操作(書込みやリサイズ)すると、
実行時エラー 10 で弾かれる仕様になっています。VBA では、配列そのもの参照を
複製する動作は一般的では無さそうです。VB.NET とは違って。



> このように、異なるsubで二つの使い方があるのは分かるのですが

オブジェクトの「参照」をコピーするだけならば、比較的高速に行えますが、
データも含めた詳細な複製(deep copy)が必要となると、データ量が多くなるにつれ
ボトルネックとなってしまうでしょう。

VBA において、ユーザー定義型を ByVal で渡せないのも、
VBA において、配列を ByVal で渡せないのも、
各要素の内容を逐次複製することがボトルネックになるためだと思います。
VBA の LSet ステートメントが使えるような、Bittable な型であれば、
データ量が多くても高速にコピー可能かもしれませんが、それは例外的なものとして。


――VBA 側の事情はさておき、配列やクラスはデータ構造が巨大になりえることから、
VB.NET では、「参照」のコピーがデフォルトの動作になっているのだと思います。
たとえば deep copy が必要なら、MemberwiseClone 等のために ICloneable を実装するということで。
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ