質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

3回答

603閲覧

C# 配列の要素と渡した変数の連動

gozi

総合スコア17

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

1クリップ

投稿2018/09/26 06:46

C#言語で配列の要素を変更したときに、格納した変数も連動して変更をかけたく試してみて分からなかったので質問させていただきました。

C#

1 #region 値型であるint 2 3 var i_hoge1 = 1; 4 var i_hoge2 = 2; 5 var i_hoge3 = 3; 6 7 var i_hoges = new int[3]; 8 9 i_hoges[0] = i_hoge1; 10 i_hoges[1] = i_hoge2; 11 i_hoges[2] = i_hoge3; 12 13 for (int i = 0; i < i_hoges.Length; ++i) { 14 Console.WriteLine(i_hoges[i]); 15 } 16 17 // 配列に格納したものの内容を変更 18 for (int i = 0; i < i_hoges.Length; ++i) { 19 i_hoges[i] = 10 + i; 20 } 21 // 値型なので連動して変わることはない 22 Console.WriteLine(i_hoge1); 23 Console.WriteLine(i_hoge2); 24 Console.WriteLine(i_hoge3); 25 26 // 逆も然り 27 i_hoge1 = 101; 28 i_hoge2 = 102; 29 i_hoge3 = 103; 30 for (int i = 0; i < i_hoges.Length; ++i) { 31 Console.WriteLine(i_hoges[i]); 32 } 33 34 #endregion 35 36 #region 参照型であるstring 37 38 var s_hoge1 = "hoge1"; 39 var s_hoge2 = "hoge2"; 40 var s_hoge3 = "hoge3"; 41 42 var s_hoges = new string[3]; 43 44 var s_hoges_ = new List<string>(); 45 46 s_hoges[0] = s_hoge1; 47 s_hoges[1] = s_hoge2; 48 s_hoges[2] = s_hoge3; 49 50 for (int i = 0; i < s_hoges.Length; ++i) { 51 Console.WriteLine(s_hoges[i]); 52 } 53 54 // 配列に格納したものの内容を変更 55 for (int i = 0; i < s_hoges.Length; ++i) { 56 s_hoges[i] = "hoge1" + i.ToString(); 57 } 58 // 参照型だから変わると思いきや変更なし 59 Console.WriteLine(s_hoge1); 60 Console.WriteLine(s_hoge2); 61 Console.WriteLine(s_hoge3); 62 63 // 逆も然り 64 s_hoge1 = "hoge101"; 65 s_hoge2 = "hoge102"; 66 s_hoge3 = "hoge103"; 67 for (int i = 0; i < s_hoges.Length; ++i) { 68 Console.WriteLine(s_hoges[i]); 69 } 70 71 #endregion

知りたいこととしましては、配列の要素を変更することで、その番号に追加した元の変数の変更ができないのかという事です。
どうぞよろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/09/26 07:04

質問は string 型で「参照型だから変わると思いきや変更なし」なのは何故かということですか? 他にもあるなら何が分からないか書いてください。
gozi

2018/09/26 07:16

申し訳ございません。少々あいまいな聞き方になっていました。 このやりたいことを実現する方法はあるかという事が知りたいです。
退会済みユーザー

退会済みユーザー

2018/09/26 07:23

「やりたいこと」というのがまたわからないのですが。「参照型だから変わると思いきや変更なし」というところが「変更あり」になってほしいということですか? であればどのようになってほしいのですか? ところで、string 型は参照型ながら、他の参照型と違って、不変性がある&インターンされるという特殊性があることはご存知ですか?
YAmaGNZ

2018/09/26 07:37

それが分かってないから、この質問だと思います。
Zuishin

2018/09/26 08:04

そういう話なんでしょうか? 配列とローカル変数を同期して変更したいという話だと思いましたが。
gozi

2018/09/26 08:44

Zuishin様の解釈が私の求めていた答えとなります。 また、いただいたコードをstring型にして試したところ意図した結果を出すことができました。なぜstring型の仕様の話を出されたのか記事を読んでみても分かりません。ただ、細かい仕様は知らなかったので勉強になりました。はっきりとした質問ができるよう気を付けていきたいと思います。ありがとうございました。
退会済みユーザー

退会済みユーザー

2018/09/26 09:21

自分は、コードのコメント「// 参照型だから変わると思いきや変更なし」というところに目が行っていて、紹介した記事のように変わらないのは当たり前なので、そこの誤解を解けば疑問は解消すると思っていたので、「配列とローカル変数を同期して変更したい」という話だとは全く思ってませんでした。
gozi

2018/09/26 12:19

stringの仕様ではなく配列とローカル変数の同期ができてないから値が変わらないという認識でいます。同じコードで参照型であるclassでも試してみたのですが、値の書き換わりは起きませんでした。classもstring型と同じで特殊なのかは分かりませんし、試し方が悪かったのかもしれませんがどうなのでしょうか。
退会済みユーザー

退会済みユーザー

2018/09/27 06:35

string の不変性にかかわる話と勘違いしてました。それ以前の話だったようですね。勘違いして間違ったコメントをしてすみません。すでに解決済みとなっていて今さらながらですが、回答欄に説明を書いておきます。
guest

回答3

0

ベストアンサー

ref 戻り値と ref ローカル変数 を読んでみてください。

C#

1using System; 2 3namespace ConsoleApp1 4{ 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 var i_hoges = new[] { 1, 2, 3 }; 10 ref var i_hoge1 = ref i_hoges[0]; 11 ref var i_hoge2 = ref i_hoges[1]; 12 ref var i_hoge3 = ref i_hoges[2]; 13 14 Print(i_hoges); 15 16 for (int i = 0; i < i_hoges.Length; i++) 17 { 18 i_hoges[i] = 10 + i; 19 } 20 21 Print(i_hoge1, i_hoge2, i_hoge3); 22 23 i_hoge1 = 101; 24 i_hoge2 = 102; 25 i_hoge3 = 103; 26 27 Print(i_hoges); 28 29 Console.ReadKey(); 30 } 31 32 static void Print(params int[] i_hoges) 33 { 34 foreach (var i in i_hoges) 35 { 36 Console.WriteLine(i); 37 } 38 } 39 } 40}

投稿2018/09/26 07:28

Zuishin

総合スコア28660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kiichi54321

2018/09/26 08:15

こういうことできるのですね。勉強になりました。
fuzzball

2018/09/26 08:38

paiza.ioでエラーになると思ったらC#6でした。7から使えるんですね。
gozi

2018/09/26 10:01

refをこのように使えばいけるのですね。動作、処理の確認しました。 これと同じ処理を7より前のバージョンで組むことは可能なのでしょうか? Unityでの開発でこの動きを実装したく考えていています。サポートが6.0までという事をすっかり失念していました。完全な回答の後、こちらの不手際での追加質問となってしまい大変恐縮ではございますが、どうぞよろしくお願いいたします。
Zuishin

2018/09/26 10:08

7 の新機能なので無理です。 何のためにこれがしたいのかがわかれば代替案が出ると思いますが、おそらくコードの書き直しは必要でしょう。 i_hoge1, i_hoge2 のように連番の変数は使うべきではなく、最初から配列を使うべきものです。
gozi

2018/09/26 12:42

何のためかと言いますと、タイルマップのようにGameObjectを配置して、それを1つずつ格納したGameObject型の配列を作り、配列の要素番号に変更をかけたときに配置したオブジェクトも連動して変更をかけたいからです。 重い通りに動かすことはできるのですが、かなり重い処理の実装しか思いつかなかったので書き直しを要しても同じ動作で動かせるなら書き直したいと考えています。 良く考えた結果ではありますが、それならそんなことする必要ないなどもありましたらお願いします。 見やすいコード書けるように善処します。
Zuishin

2018/09/26 13:24

それだけじゃよくわかりませんね。 オブジェクトのプロパティを変更するか、独立した変数を使わず配列のみを使えばいいように見えます。
gozi

2018/09/26 15:27

あまり聞くことにも慣れておらず、分かりにくくて申し訳ございません。 ですが、GameObject型の配列[index]番目に変更をかけたときに、index番目に格納したGameObjectの値も変わるようにしたい以上何をお伝えしたらわかりやすいのか見当がつきません。 私が根本から勘違いしているかもしれないので念のために… GameObject gameobject; GameObject[] gameobjects = new GameObject[size]; gameobjects[index] = gameobject; があった時に、gameobjects[index].SetActive(false);とかなんでも変更をかけたとしても、index番目に入れたgameobjectには変更はかかりませんよね? ここに変更がかかってほしくて質問をさせていただきました。 このやり方でやってみようと考えるまでは、配置するGameObjectの名前に番号をつけて、名前を検索して探すしかないと考えていました。毎フレーム入る処理で想定しているので、名前で探すよりそのまま配列の番号に格納されているGameObjectにアクセスするだけで済むのならばそのほうが処理も軽減させれると考えました。
Zuishin

2018/09/26 15:55

インスタンスを同期して取り換えることはできません。 変更がかかるというのがどういうことかわかりませんが、同じインスタンスを保持しているならプロパティの変更はできます。 また、各インスタンスに独立した変数を使わず、配列のみでアクセスすればいいと思います。 「変更がかかる」のような独自の用語で一言で表されてもわかりません。コメント欄ではなく質問を編集してコードを書いて他人にわかるよう説明してください。
asm

2018/09/26 15:56

> 変更はかかりませんよね? 実験はされましたか? 適当な実験の結果オブジェクトの操作は同期される、という結果になりました (同一オブジェクトなので) https://wandbox.org/permlink/NW30P7k4NFA1YK3S
gozi

2018/09/27 00:49 編集

題をC#として、Unityの話になってしまっているので、テストしコードを書いてここでお教えいただいたことを参考にしても分からなかった場合にまた別枠で質問させていただこうと考えています。 Zuishin様のおっしゃることは現状ではわかりかねますが、おそらくは私の勘違いでとても簡単なところで詰まっているように感じます。配列のみで扱えることを前提としてテストの方進めていきます。 asm様 実験はしましたが、やり方が少し悪かったようでした。 貼っていただいたコードを参考にやってみたら同期されること確認いたしました。 この質問ページは、C#7の機能であるrefを使って参照を格納してあげたら、値型でも配列とローカル変数の同期がとれる。ということで決着とさせていただきます。 ありがとうございました。また機会がありましたらどうぞよろしくお願いいたします。
guest

0

質問に対する私のコメントで「回答欄に説明を書いておきます」と書きましたが、それを以下に書いておきます。今さら不要でしたらスルーしてください。

不変性の話はちょっと置いといて、それ以前の話してして、質問のコードの、

// 参照型だから変わると思いきや変更なし
// 逆も然り

というところだけを考えます。簡単のために s_hoge1 と s_hoges[0] だけに注目すると、

var s_hoge1 = "hoge1"; s_hoges[0] = s_hoge1; s_hoges[0] = "hoge1" + "0"; Console.WriteLine(s_hoge1); // "hoge10" になると思ったが "hoge1" s_hoge1 = "hoge101"; Console.WriteLine(s_hoges[0]); // "hoge101" になると思ったが "hoge10"

という話ですよね?

上のコードの最初の var s_hoge1 = "hoge1"; で何をしているかと言うと下の画像と同様なことをしています。s_hoge1 が s に該当します。

イメージ説明

s_hoges[0] は s_hoge1 とは別にメモリ上に確保された変数です。上の画像に沿ってもっと簡略化すると質問者さんのコードは以下と同等になるはずです。s_hoge1 が s に s_hoges[0] が t に該当します。

string s = "hoge1"; string t = s; t = "hoge1" + "0"; Console.WriteLine(s); // 結果は "hoge1" s = "hoge101"; Console.WriteLine(t); // 結果は "hoge10"

上記を見れば、私が一番最初のコメントで書いた、

「参照型だから変わると思いきや変更なし」なのは何故かということですか?

の答えにはなると思うのですがいかがでしょうか?

【追記】

ついでに string 型の不変性の話を書いておきます。下のコードを見てください。

string s1 = "hoge1"; string t1 = s1; s1 += "0"; Console.WriteLine(t1); // 結果は "hoge1"

string は参照型なので、上記の s1 += "0"; で、変数 s1 が指すオブジェクトを "hoge1" ⇒ "hoge10" を変更する、即ちオブジェクトのアドレスは変わらないと思うかも知れませんね。

でも、不変性があるのでオブジェクトは変更できません。どうしているかと言うと、"hoge10" というオブジェクトを新たに作って(当然 "hoge1" とはアドレスが違う)それへの参照を s1 に代入します。

一方、t1 は "hoge1" を指したままのので Console.WriteLine(t1); の結果は "hoge1" になります。

投稿2018/09/27 06:43

編集2018/09/27 07:02
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fuzzball

2018/09/27 07:02

質問者は一貫して「実現する方法」を聞いているのに、SurferOnWwwさんはずっと「何故そうなるのか」についてコメント/回答しているから、話が噛み合ってないように見えます。
退会済みユーザー

退会済みユーザー

2018/09/27 07:11

「何故そうなるのか」を理解するのが最初のステップになると思っているのですが。基本のキに近いことで、理解しているのとしてないのとでは大違いと思っています。質問者さんが言われる「おそらくは私の勘違いでとても簡単なところで詰まっているように感じます」というのはそこではないですか?
fuzzball

2018/09/27 07:40

追記修正欄でのやりとりを見ていると、その思いを蹴り返しているように見えるんですよね。「難しいことはいいから実現する方法を教えろ」と。 「細かい仕様は知らなかったので勉強になりました」とのコメントがありましたが、おそらく上っ面しか理解していない(理解しようとしていない)んじゃないかと。 「string型は不変である」ことは理解できても、それが「参照先が変わる」ことに繋がっていない。 この回答を見て理解が進めばいいなぁと思っています。
gozi

2018/09/27 08:35

fuzzballさんはちょっと何を言っているのか、何がしたいのか分からないのでスルーさせていただきますね。 いろいろ試してみて、すべて解決できたのですが、とても簡単なところというのは全く別のところでした。 string型が特殊であるのは記事を貼っていただいた地点で読みましたし、classで試して値が変わることが確認できた地点で、SurferOnwww様がstringの仕様の話を持ち出したことにも合点がいきました。 勘違いさせてしまったのもstringの仕様を知らずにstring型でテストコードを乗せた私の落ち度です。 ですが、これのおかげで勉強不足であった基本中の基本を押さえることができたので大変感謝しています。
guest

0

class Text { public string Value{get;set;} }

こういうオブジェクトを使えば、期待通りの動きをすると思う。
細かいこと忘れたけど、Stringは、最適化のため、参照型としては特殊なことをしている。

投稿2018/09/26 07:10

kiichi54321

総合スコア1984

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/09/26 07:25

「期待通りの動き」というのは kiichi54321 さんの理解ではどういったものなのですか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問