Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Unimaginable code & commentary

199 views

Published on

2018/10/13 Documents spoken at Yokkaichi, Mie Prefecture

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Unimaginable code & commentary

  1. 1. 結果がイメージしにくいコードと 実行結果 for .net 2018年10月13日 第16回 まどべんよっかいち 可知 一輝
  2. 2. 自己紹介  ひとりでやってます(ふりーらんすえんじにあとか言うらしい)  SNS Accounts  Facebook:Kazuki.Kachi  Twitter :@kazuki_Kachi  Microsoft MVP for Developer Technologies(2017-2019)
  3. 3. 今日は、ネタがなかった参加者が意外にかぶって いたので8月に話した微妙にわかりにくいコードの 実行結果の解説をしてみる時間です。
  4. 4. 当時書いた前提はこちら • 正解しても商品とかはありません。 • これどうなるんでしたっけ?的なコードを集めた(つもり) • 間違えても誰にも怒られたりはしませn。 • 疑問に思った事は聞いてください(誰かが答えてくれるハズ) • 実行環境は、.Net core 2.1(Windows10 1803)です。
  5. 5. とはいえ、参加してない方もいらっしゃいますし、参加 していたとしても覚えていない方が大半だと思います。 当時使用したコードと実行結果をもとに進めていきます。 ※コードは一部省略しています。 ※using ブロックはすべて省略
  6. 6. 結果はどうなる?(その1) static void AddVisualBasic(List<string> target) => target?.Add("VisualBasic"); static void Main(string[] args) { : var languages = new List<string>() { "C#" }; AddVisualBasic(languages); WriteLine(languages); }
  7. 7. 結果はこうなる(その1) System.Collections.Generic.List`1[System.String]
  8. 8. 何故こうなる(その1) object classのToString()Methodの実装が、 return GetType().ToString(); となっており、List<T>classはToString()Methodをoverrideしていないため。 Javaなんかだと、内部の値を表示してくれたりするけど、C#はそこまでしてくれない。
  9. 9. 結果はどうなる?(その2) static void AddVisualBasic(List<string> target) => target?.Add("VisualBasic"); static void Main(string[] args) { : var languages = new List<string>() { "C#" }; AddVisualBasic(languages); languages.ForEach(WriteLine); }
  10. 10. 結果はこうなる(その2) C# VisualBasic
  11. 11. 何故こうなる(その2) C#では、キーワードを指定せずMethodに引数を与えた場合、stackのコピーが渡される。 classの場合、stackにinstanceではなくinstanceの場所(Address)が格納されているため、 instanceに対する変更は呼び出し元に伝搬される。
  12. 12. 結果はどうなる?(その3) { : void changeList(List<string> target) { target = new List<string> { "VisualBasic" }; }; var languages = new List<string>() { "C#" }; changeList(languages); languages.ForEach(WriteLine); }
  13. 13. 結果はこうなる(その3) C#
  14. 14. 何故こうなる(その3) C#では、キーワードを指定せずMethodに引数を与えた場合、stackのコピーが渡される。 この場合のtargetはあくまでlanguagesのAddressが格納されてはいるものの、 languegesとは別なのでtargetを書き換えても呼び出し元には伝搬しない。
  15. 15. 結果はどうなる?(その4) interface IValue<T> { T Value { get; set; } } struct ValueStruct<T> : IValue<T> { public T Value { get; set; } public override string ToString() => $"Value is {Value?.ToString() ?? "null"}!"; } { : void changeValue<T>(ValueStruct<T> target, T value) => target.Value = value; var v = new ValueStruct<int>() { Value = 5 }; changeValue(v, 100); WriteLine(v); }
  16. 16. 結果はこうなる(その4) Value is 5!
  17. 17. 何故こうなる(その4) C#では、キーワードを指定せずMethodに引数を与えた場合、stackのコピーが渡される。 structの場合、stackにinstanceが格納されているため、 instanceに対する変更はあくまでもコピーに対する変更のため、呼び出し元に伝搬されない。
  18. 18. 結果はどうなる?(その5) interface IValue<T> { T Value { get; set; } } struct ValueStruct<T> : IValue<T> { public T Value { get; set; } public override string ToString() => $"Value is {Value?.ToString() ?? "null"}!"; } { : void changeValue<T>(IValue<T> target, T value) => target.Value = value; var v = new ValueStruct<int>() { Value = 5 }; changeValue(v, 100); WriteLine(v); }
  19. 19. 結果はこうなる(その5) Value is 5!
  20. 20. 何故こうなる(その5) C#では、キーワードを指定せずMethodに引数を与えた場合、stackのコピーが渡される。 interfaseの場合はclass同様、stackにinstanceのaddressが格納されているが、 作成したinstance(宣言した変数の型)はstructのため、stackがコピーされた後、参照が渡されるため、 結果instanceに対する変更コピーに対する変更になり、呼び出し元に伝搬されない。 余談ですが、先のコードの、 var v = new ValueStruct<int>() { Value = 5 }; を IValue v = new ValueStruct<int>() { Value = 5 }; に変更すると、 vは参照型の変数になるため変更結果が伝搬する
  21. 21. 結果はどうなる?(その6) { : var i = 0; void output() => WriteLine(i); i = 100; output(); }
  22. 22. 結果はこうなる(その6) 100
  23. 23. 何故こうなる(その6) Local関数(lambda式含む)は、実際にはprivateなMethodとして生成され、実行される。 この場合、output()はprivate staticなMethodとして展開され、 int iはoutputから参照できるスコープに移動される(VisualStudioを使用している限り意識することはない) そのため、output()を実行する前にi の値を変更したら、変更後の値が出力される (展開イメージ(実際の展開結果ではありません)) static int i; static void Output() => WriteLine(i); { i = 100; Output(); }
  24. 24. 結果はどうなる?(その7) { : var i = 0; Action output(int value) => () => WriteLine(value); var act = output(i); i = 100; act(); }
  25. 25. 結果はこうなる(その7) 0
  26. 26. 何故こうなる(その7) このケースでは、int i を int value に渡した時点のi と value の関係は、 value は output()を呼び出した時点のi の値のコピーであるため、その後iをいくら変更しても Valueには伝搬しない。
  27. 27. 結果はどうなる?(その8) { : var i = 0; Action act = () => WriteLine(i); while(10 > ++i) act += () => WriteLine(i); act(); }
  28. 28. 結果はこうなる(その8) 10 10 10 10 10 10 10 10 10 10
  29. 29. 何故こうなる(その8) その6に書いたことと同じ理由。
  30. 30. 結果はどうなる?(その9) { : Action act = null; foreach(var i in Enumerable.Range(0, 10)) act += () => WriteLine(i); act(); }
  31. 31. 結果はこうなる(その9) 0 1 2 3 4 5 6 7 8 9
  32. 32. 何故こうなる(その9) iは、ループのたびに生成され、解放されている。 すなわちWriteLineが最初に受取ったiと次に受取ったiは別のものとしてキャプチャされる。 (foreachの展開イメージ) using(var ie = Enumerable.Range(0, 10).GetEnumerator()) { while(ie.MoveNext()) { var i = ie.Current; act += () => WriteLine(i); } }
  33. 33. 結果はどうなる?(その10) Shared Sub Q10() WriteLine(1 / 2) End Sub
  34. 34. 結果はこうなる(その10) 0.5
  35. 35. 何故こうなる(その10) VisualBasicのoperator /(Integer,Integer)の戻り値の方はDouble。 …察してください。 ちなみにVBでInteger型の商を得る演算子は、(Integer,Integer)
  36. 36. ここで紹介できたコードは、実行結果がイメージと違う かもしれないコードのほんの一部です。 しかも、該当箇所を抜粋しています。 書きたくなる場面もあるかもしれませんが、 未来の自分のためにもなるべく書かないことを押すsumし ます。
  37. 37. ありがとうございました。

×