O slideshow foi denunciado.
Utilizamos seu perfil e dados de atividades no LinkedIn para personalizar e exibir anúncios mais relevantes. Altere suas preferências de anúncios quando desejar.

Unimaginable code & commentary

479 visualizações

Publicada em

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

Publicada em: Tecnologia
  • Entre para ver os comentários

  • Seja a primeira pessoa a gostar disto

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. ありがとうございました。

×