Unityで使える C# 6.0~と .NET 4.6

763 views

Published on

http://peatix.com/event/311392 にて登壇。

Unity 2017 で、C# 6.0が使えるようになりました。ま

Published in: Technology
  • Be the first to comment

Unityで使える C# 6.0~と .NET 4.6

  1. 1. Unityで使える C# 6.0~と .NET 4.6 岩永 信之
  2. 2. はじめに • 普通に使う分にはnet46/C#6.0使っとけばいいと思う • しんどいのは 1. Unityの外でビルドしたDLLの利用 • 特に、NuGet.org からのパッケージ参照 • net35とnet46の混在(社内にUnity5系プロジェクトが残ってる) 2. 互換ライブラリの移植 • net4以降のライブラリをnet35向けに移植して使ってた • 移植漏れで本家と微妙に挙動が違う
  3. 3. 今日話すこと • net46化、C# 6.0 (以降)化のメリットをちょこっと • 「しんどいの」の話とその対処
  4. 4. C# 6.0/.NET 4.6
  5. 5. 8年分のアップデート 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 C# 3.0/.NET 3.5 C# 6.0/.NET 4.6 約8年 LINQ C# 4.0 C# 5.0 C# 7.0 C# 7.1 C# 7.2 dynamic async/await 細々とたくさん (Roslyn) タプル パターン マッチ ref + 構造体C# 3~6の間の変化としては これが一番大きい パフォーマンス改善 ゲーム開発的にはこ れの方が欲しいかも
  6. 6. ということでasync/await • 非同期処理を同期っぽく書ける • ちゃんとコールバック呼び出しに変換される • スレッドをブロックしない var c = new HttpClient(); var res = await c.GetAsync("http://ufcpp.net"); var content = await res.Content.ReadAsStringAsync(); 例 awaitって書くだけで 非同期処理を 同期っぽく待てる この間、UIスレッドを止めない ゲームがフリーズしない • ゲームではフリーズしないの大事 • スマホゲーなんて通信だらけ • await大事
  7. 7. Taskクラス • async/awaitのお供にTaskクラス • (System.ThreadingTasks名前空間) • ちなみに • 所定のパターンを満たせばTask以外でもawait可能 • でも、たいていの場面ではTaskでOK var c = new HttpClient(); var res = await c.GetAsync("http://ufcpp.net"); var content = await res.Content.ReadAsStringAsync(); Task<HttpResponseMessage> GetAsync(string requestUri);
  8. 8. Taskとコルーチン • Q. コルーチンではダメ? • UnityEngine.dllの参照要らない(Unityの外とのコード共有) • 非同期I/O向けにはTaskの方が効率いい • コルーチンは本来「毎フレーム1回Updateが呼ばれるような処理」向け • 戻り値の受け渡しがだいぶ楽 var c = new HttpClient(); var res = await c.GetAsync("http://ufcpp.net"); var content = await res.Content.ReadAsStringAsync(); 戻り値! さっきの例
  9. 9. TaskとJob System • Unity、今度Job Systemとかいうの入れるって言ってるけど? • (自称)速いらしいけど… • 用途が絞られてる上に、結構特殊処理っぽい • Parallel†代わりにはなってもTask代わりにはならない 並 列 計 算 非 同 期 I / O † System.Threading.Tasks名前空間の静的クラス。 並列Forとか並列ForEachメソッドを持ってる
  10. 10. TaskとRx • Q. Rxとの違いは? • Rxの債務は2つある • Taskと同じ、戻りを1個受け取るタイプの非同期処理 • イベント • 前者の用途ではUnity以外ではあんまり流行らなかった • Taskの方が受け入れられやすかった • Q. Taskに移行した方がいい? • A. No • UniRxのままでawaitできる • 所定のパターンを実装していれば何でもawaitできる • UniRxは当然実装してる† † https://github.com/neuecc/UniRx/blob/master/Assets/Plugins/UniRx/Scripts/Observable.Awaiter.cs
  11. 11. 弊社内の話: Task • .NET 3.5向けの移植ライブラリを作って使ってる • Unity 4系の頃からずっともうawait使ってる • 5年くらい運用 • Unityエディター内で使えないんで、外でビルド • DLL化してからUnity内にコピー • コピー用のPostBuildテンプレートを使ってる • Unityエディター内ではコルーチンとの相互変換を提供 • await使えないとか無理
  12. 12. await以外におすすめC#新機能は? • 4.0 … dynamicはスマホでは使いにくいので無視でOK • 6.0 … 小さくて便利な改善が多々 • できることが増えるわけじゃない • 既存機能の書き心地が良くなる類 • 1個1個紹介してたら切りがないぐぐれ • 「最新をキャッチアップしましょう」とか意識高いこと言う気ない そんなずぼらなあなたに朗報 Visual Studio 2015/2017が最新機能を教えてくれます!
  13. 13. Visual Studioが教えてくれます • こんなの • C# 6.0以降、「今のC#ならこう書けるよ」的な提案が出てきて 機械的にコード書き換えてくれる 一部紹介していきます(当然のようにVS 2017で) クイック アクション (電球マーク) • 古い書き方を新しい書き方 に自動で書き換えてくれる • 書き換えの前後を目視確認 できる
  14. 14. C# 6.0: 式形式メンバー int F(int x) { return x * x; } int F(int x) => x * x; 変更前 変更後 • return 1つだけのメソッドを =>形式に変更 • 可逆(戻すアクションあり)
  15. 15. C# 6.0: nameof演算子 void F(string str) { } void F(string str) { if (str == null) { throw new ArgumentNullException(nameof(str)); } } 変更前 変更後 • 引数のnullチェックを追加 • 引数名はnameof演算子で 取れる
  16. 16. C# 6.0: 文字列補間 string.Format("({0}, {1})", x, y) $"({x}, {y})" 変更前 変更後 • $から始まる文字列を書くと {}内に値を展開できる
  17. 17. C# 6.0: null条件演算子 obj != null ? obj.ToString() : null; if (a != null) a(); obj?.ToString(); a?.Invoke(); 変更前 変更後 • ?. 演算子でnullを伝搬 • [] もOK • () はダメなので ?.Invoke
  18. 18. C# 7.0: パターン(型スイッチ) var d = obj as IDisposable; if (d != null) { d.Dispose(); } if (obj is IDisposable d) { d.Dispose(); } 変更前 変更後 • as + nullチェックを is 1個で書ける
  19. 19. おまけ: コンストラクター生成 class Point { public int X { get; } public int Y { get; } } class Point { public int X { get; } public int Y { get; } public Point(int x, int y) { X = x; Y = y; } } 変更前 変更後 • プロパティからコンストラクター を作れる • 逆も可(コンストラクター引数か らプロパティ)
  20. 20. 弊社の話: 新機能への追従 • Task同様「Unityの外でビルド」で元からC# 7.1使ってる • Q. バージョン上がるたびに社内啓蒙とか必要? • A. awaitくらい大きな機能ならともかく、細かいものはNo • 新機能は使いたい人が使えばいいと思ってる • 自分は容赦なく使ってるけど、自分が使ってれば周りは割とついてくる • クイック アクションがあるものは、割とみんなすぐに使いだす • IDEは学習ツールでもある
  21. 21. その他、net46のメリット • NuGetパッケージの利用 • 最近さすがにnet35サポートを切ったライブラリが増えてる • Task(async/await)が真っ当に使えるのはnet45から • netstandard1.0 = net45 • Windows 8以降、net35は標準インストールされていない ↓ • net46なら使えるライブラリの幅が大きく増える
  22. 22. 移行で苦労した話
  23. 23. 始めに: C#の互換性 • 言語としては、C#はすごく互換性しっかりしてる • ソースコードレベルだと、大概大丈夫 • 学生時代に書いたC# 2.0コード、そのままで今もビルドできた • Unityで、C# 3.0 → 6.0 程度の変化はどうということない • ソースコードならね… • しんどくなる原因 • Unityの外と共有/外でDLL化 • Unity 5系が残ってるので、共通コードは.NET 3.5/4.6両対応 • NuGetパッケージをnuget.orgから取ってきて使おうとしてる
  24. 24. 苦労の理由1: Unityの外でDLL化 • メリット • C# 7.1も使える • Unityビルド時間短縮 • nuget.orgからライブラリを取ってこれる • デメリット • ビルドに1手間かかる • ビルド後、DLLをUnity配下にコピー • デバッグ実行のためにcsprojを書き換えたり mdb生成したり • Unityのバグをよく踏む… • やってる人が少ないせい 社内共通ライブラリの net46化に苦労したのも 大体この辺り 踏んだ問題をいくつか紹介
  25. 25. TargetFramework net46 • TargetFrameworkをnet46に変えないとうまく動かなかった • 補足: 本来、net46はnet35の上位互換のはずなのに… • たぶん、標準ライブラリのDLL整理をしたせい net35の頃 = monolithic net4以降 = fine-grained int, string, DateTime List<T>, HashSet<T> Thread File mscorlib int, string, DateTime List<T>, HashSet<T> Thread File System.Runtime System.Collections System.Threading System.IO
  26. 26. TargetFramework net46 • 対処 <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <LangVersion>latest</LangVersion> <TargetFramework>net35</TargetFramework> <DebugType Condition="'$(Configuration)'=='DEBUG'">full</DebugType> </PropertyGroup> </Project> 共通ライブラリのcsproj 元 補足: SDK-based csproj Skd属性を付けておくとcsprojが単純化できる (Visual Studio 2017以降の機能) SDK-basedにするとデバッグ情報の形式が変わる (Unityが未対応) しょうがないから古い形式で出力する設定追加
  27. 27. TargetFramework net46 • 対処 <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <LangVersion>latest</LangVersion> <TargetFrameworks>net35;net46</TargetFrameworks> <DebugType Condition="'$(Configuration)'=='DEBUG'">full</DebugType> </PropertyGroup> </Project> 共通ライブラリのcsproj 書き換え内容 ① TargetFrameworkタグを TargetFrameworks (複数形)に ② ; 区切りでnet46を追加† † Unity5系のプロジェクトも社内に残ってるのでnet35サポートは切れない
  28. 28. System.Runtime.dll等の参照 • 「アセンブリが見つからない」実行時エラー • NuGetでライブラリ参照したとき • Android/iOSビルドでだけ問題が起きたりする • 対処 • 同名のDLLをMonoBleedingEdgeフォルダーからコピーしてくる net4以降 = fine-grained int, string, DateTime List<T>, HashSet<T> Thread File System.Runtime System.Collections System.Threading System.IO この辺りのDLLが標準で コピーされない不具合† † バグ報告が出ているので、そのうち治ってくれるはず
  29. 29. 苦労の理由2: 自前の互換ライブラリ • 使ってた互換ライブラリ • net35時代にTask(async/await)を使うために • MinimumAsyncBridge • System.Threading.Tasksのnet35向けバックポート • 本家RxとUniRx混在 • UniRxをフォークして手を入れてる • net46になったし • 互換ライブラリを捨てたい • 本家に以降 移植漏れで本家と微妙に挙動が違う • 主に、マルチスレッド絡み (UIスレッドに戻るタイミングが違う等)
  30. 30. 苦労の理由2: 自前の互換ライブラリ • 移植漏れで本家と微妙に挙動が違う • 対処 • トライ&エラーしかなかった… • 例 • UIスレッド絡みの挙動は実行時エラーを起こす → 実行時エラーを見つけては、UIスレッドに戻す処理を手で追加 • 内部的にリフレクションを使っていて呼べないメソッドがある → 実行時エラーを見つけては、そのメソッドを別のオーバーロードに置き換え
  31. 31. おまけ: その他今踏んでるバグ C# 7.2が使いたくて困ってる話 Unity側に対応してもらう以外どうしようもなさげ
  32. 32. タプルが使いたい • C# 7.0からの機能 • ()で匿名の構造体を作れる構文 • 内部的にValueTuple構造体に依存 • Unityの問題 • 症状: Androidでだけ例外を起こす • 原因: Unityが.NET Standardなライブラリに対応していない • 状況: 2018.1で.NET Standardに対応するって言ってる var t = (1, 2); var (x, y) = t; var t = (1, 2); var t = new ValueTuple<int, int>(1, 2);
  33. 33. Visual Studio 15.5を使いたい • 最新のプレビュー版 • C# 7.2(プレビュー)が使える • ソリューションのロード時間が数倍早くなってる • 今、社内ライブラリで問題になっている不具合が修正されたっぽい • Unityの問題 • 症状: 特定のコードでUnity Editorが即死 • 原因: ポインターの扱いがsignedからunsignedに変わった • C#コンパイラーの挙動変更(パフォーマンス改善のため) • Unityが使ってるバージョンのMonoが命令変更に対応していないっぽい • 状況: 調査中とのこと string s; fixed(char* p = s) { }
  34. 34. Span<T>を使いたい • C# 7.2世代で同時に追加される予定の構造体 • パフォーマンス改善にかなり効く • 内部的にUnsafeクラス†を利用 • Unityの問題 • 症状: Androidでだけ例外を起こす • 原因: UnsafeクラスはC#じゃなくて、ILアセンブラーで書かれてる • Unityが使ってるバージョンのMonoが(C#では書けない)命令に対応していないっ ぽい • 状況: 調査中とのこと† https://www.nuget.org/packages/System.Runtime.CompilerServices.Unsafe/ void SafeMethod() { Span<byte> buffer = stackalloc byte[256]; } これまでunsafeコード必須だっ たような最適化を安全に書ける
  35. 35. まとめ
  36. 36. まとめ • Unityで閉じてる分にはnet46化もそんなに大変じゃない • C#は後方互換しっかりしてる • Unityの外でDLL作る/外からDLLを持ってくるとちょっと大変 • C# 7.1を使う • nuget.orgから取得 • 互換ライブラリの移植で多少苦労 • 本家との微妙な挙動差をトライ&エラーでちまちま修正 • Visual Studio 15.5/C# 7.2化でだいぶ困ってる • Unity側の対応待ち(自前では回避できなさそう)
  37. 37. http://www.cryuni.com/

×
Save this presentationTap To Close