SlideShare a Scribd company logo
Unityで使える
C# 6.0~と .NET 4.6
岩永 信之
はじめに
• 普通に使う分にはnet46/C#6.0使っとけばいいと思う
• しんどいのは
1. Unityの外でビルドしたDLLの利用
• 特に、NuGet.org からのパッケージ参照
• net35とnet46の混在(社内にUnity5系プロジェクトが残ってる)
2. 互換ライブラリの移植
• net4以降のライブラリをnet35向けに移植して使ってた
• 移植漏れで本家と微妙に挙動が違う
今日話すこと
• net46化、C# 6.0 (以降)化のメリットをちょこっと
• 「しんどいの」の話とその対処
C# 6.0/.NET 4.6
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の間の変化としては
これが一番大きい
パフォーマンス改善
ゲーム開発的にはこ
れの方が欲しいかも
ということでasync/await
• 非同期処理を同期っぽく書ける
• ちゃんとコールバック呼び出しに変換される
• スレッドをブロックしない
var c = new HttpClient();
var res = await c.GetAsync("http://ufcpp.net");
var content = await res.Content.ReadAsStringAsync();
例
awaitって書くだけで
非同期処理を
同期っぽく待てる
この間、UIスレッドを止めない
ゲームがフリーズしない
• ゲームではフリーズしないの大事
• スマホゲーなんて通信だらけ
• await大事
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);
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();
戻り値!
さっきの例
TaskとJob System
• Unity、今度Job Systemとかいうの入れるって言ってるけど?
• (自称)速いらしいけど…
• 用途が絞られてる上に、結構特殊処理っぽい
• Parallel†代わりにはなってもTask代わりにはならない
並 列 計 算 非 同 期 I / O
† System.Threading.Tasks名前空間の静的クラス。
並列Forとか並列ForEachメソッドを持ってる
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
弊社内の話: Task
• .NET 3.5向けの移植ライブラリを作って使ってる
• Unity 4系の頃からずっともうawait使ってる
• 5年くらい運用
• Unityエディター内で使えないんで、外でビルド
• DLL化してからUnity内にコピー
• コピー用のPostBuildテンプレートを使ってる
• Unityエディター内ではコルーチンとの相互変換を提供
• await使えないとか無理
await以外におすすめC#新機能は?
• 4.0 … dynamicはスマホでは使いにくいので無視でOK
• 6.0 … 小さくて便利な改善が多々
• できることが増えるわけじゃない
• 既存機能の書き心地が良くなる類
• 1個1個紹介してたら切りがないぐぐれ
• 「最新をキャッチアップしましょう」とか意識高いこと言う気ない
そんなずぼらなあなたに朗報
Visual Studio 2015/2017が最新機能を教えてくれます!
Visual Studioが教えてくれます
• こんなの
• C# 6.0以降、「今のC#ならこう書けるよ」的な提案が出てきて
機械的にコード書き換えてくれる
一部紹介していきます(当然のようにVS 2017で)
クイック アクション
(電球マーク)
• 古い書き方を新しい書き方
に自動で書き換えてくれる
• 書き換えの前後を目視確認
できる
C# 6.0: 式形式メンバー
int F(int x)
{
return x * x;
}
int F(int x) => x * x;
変更前
変更後
• return 1つだけのメソッドを
=>形式に変更
• 可逆(戻すアクションあり)
C# 6.0: nameof演算子
void F(string str)
{
}
void F(string str)
{
if (str == null)
{
throw new ArgumentNullException(nameof(str));
}
}
変更前
変更後
• 引数のnullチェックを追加
• 引数名はnameof演算子で
取れる
C# 6.0: 文字列補間
string.Format("({0}, {1})", x, y)
$"({x}, {y})"
変更前
変更後
• $から始まる文字列を書くと
{}内に値を展開できる
C# 6.0: null条件演算子
obj != null ? obj.ToString() : null;
if (a != null) a();
obj?.ToString();
a?.Invoke();
変更前
変更後
• ?. 演算子でnullを伝搬
• [] もOK
• () はダメなので ?.Invoke
C# 7.0: パターン(型スイッチ)
var d = obj as IDisposable;
if (d != null)
{
d.Dispose();
}
if (obj is IDisposable d)
{
d.Dispose();
}
変更前
変更後
• as + nullチェックを
is 1個で書ける
おまけ: コンストラクター生成
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;
}
}
変更前
変更後
• プロパティからコンストラクター
を作れる
• 逆も可(コンストラクター引数か
らプロパティ)
弊社の話: 新機能への追従
• Task同様「Unityの外でビルド」で元からC# 7.1使ってる
• Q. バージョン上がるたびに社内啓蒙とか必要?
• A. awaitくらい大きな機能ならともかく、細かいものはNo
• 新機能は使いたい人が使えばいいと思ってる
• 自分は容赦なく使ってるけど、自分が使ってれば周りは割とついてくる
• クイック アクションがあるものは、割とみんなすぐに使いだす
• IDEは学習ツールでもある
その他、net46のメリット
• NuGetパッケージの利用
• 最近さすがにnet35サポートを切ったライブラリが増えてる
• Task(async/await)が真っ当に使えるのはnet45から
• netstandard1.0 = net45
• Windows 8以降、net35は標準インストールされていない
↓
• net46なら使えるライブラリの幅が大きく増える
移行で苦労した話
始めに: C#の互換性
• 言語としては、C#はすごく互換性しっかりしてる
• ソースコードレベルだと、大概大丈夫
• 学生時代に書いたC# 2.0コード、そのままで今もビルドできた
• Unityで、C# 3.0 → 6.0 程度の変化はどうということない
• ソースコードならね…
• しんどくなる原因
• Unityの外と共有/外でDLL化
• Unity 5系が残ってるので、共通コードは.NET 3.5/4.6両対応
• NuGetパッケージをnuget.orgから取ってきて使おうとしてる
苦労の理由1: Unityの外でDLL化
• メリット
• C# 7.1も使える
• Unityビルド時間短縮
• nuget.orgからライブラリを取ってこれる
• デメリット
• ビルドに1手間かかる
• ビルド後、DLLをUnity配下にコピー
• デバッグ実行のためにcsprojを書き換えたり
mdb生成したり
• Unityのバグをよく踏む…
• やってる人が少ないせい
社内共通ライブラリの
net46化に苦労したのも
大体この辺り
踏んだ問題をいくつか紹介
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
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が未対応)
しょうがないから古い形式で出力する設定追加
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サポートは切れない
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が標準で
コピーされない不具合†
† バグ報告が出ているので、そのうち治ってくれるはず
苦労の理由2: 自前の互換ライブラリ
• 使ってた互換ライブラリ
• net35時代にTask(async/await)を使うために
• MinimumAsyncBridge
• System.Threading.Tasksのnet35向けバックポート
• 本家RxとUniRx混在
• UniRxをフォークして手を入れてる
• net46になったし
• 互換ライブラリを捨てたい
• 本家に以降
移植漏れで本家と微妙に挙動が違う
• 主に、マルチスレッド絡み
(UIスレッドに戻るタイミングが違う等)
苦労の理由2: 自前の互換ライブラリ
• 移植漏れで本家と微妙に挙動が違う
• 対処
• トライ&エラーしかなかった…
• 例
• UIスレッド絡みの挙動は実行時エラーを起こす
→ 実行時エラーを見つけては、UIスレッドに戻す処理を手で追加
• 内部的にリフレクションを使っていて呼べないメソッドがある
→ 実行時エラーを見つけては、そのメソッドを別のオーバーロードに置き換え
おまけ: その他今踏んでるバグ
C# 7.2が使いたくて困ってる話
Unity側に対応してもらう以外どうしようもなさげ
タプルが使いたい
• 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);
Visual Studio 15.5を使いたい
• 最新のプレビュー版
• C# 7.2(プレビュー)が使える
• ソリューションのロード時間が数倍早くなってる
• 今、社内ライブラリで問題になっている不具合が修正されたっぽい
• Unityの問題
• 症状: 特定のコードでUnity Editorが即死
• 原因: ポインターの扱いがsignedからunsignedに変わった
• C#コンパイラーの挙動変更(パフォーマンス改善のため)
• Unityが使ってるバージョンのMonoが命令変更に対応していないっぽい
• 状況: 調査中とのこと
string s;
fixed(char* p = s) { }
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コード必須だっ
たような最適化を安全に書ける
まとめ
まとめ
• Unityで閉じてる分にはnet46化もそんなに大変じゃない
• C#は後方互換しっかりしてる
• Unityの外でDLL作る/外からDLLを持ってくるとちょっと大変
• C# 7.1を使う
• nuget.orgから取得
• 互換ライブラリの移植で多少苦労
• 本家との微妙な挙動差をトライ&エラーでちまちま修正
• Visual Studio 15.5/C# 7.2化でだいぶ困ってる
• Unity側の対応待ち(自前では回避できなさそう)
http://www.cryuni.com/
Unityで使える C# 6.0~と .NET 4.6

More Related Content

PDF
新入社員のための大規模ゲーム開発入門 サーバサイド編
PDF
C# ゲームプログラミングはホントにメモリのことに無頓着でいいの?
PPTX
Effective Modern C++ 勉強会 Item 22
PDF
規格書で読むC++11のスレッド
PDF
UniTask入門
PDF
Javaのログ出力: 道具と考え方
PDF
ゲーム開発者のための C++11/C++14
PDF
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
新入社員のための大規模ゲーム開発入門 サーバサイド編
C# ゲームプログラミングはホントにメモリのことに無頓着でいいの?
Effective Modern C++ 勉強会 Item 22
規格書で読むC++11のスレッド
UniTask入門
Javaのログ出力: 道具と考え方
ゲーム開発者のための C++11/C++14
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する

What's hot (20)

PPTX
async/await のしくみ
PDF
インフラエンジニアってなんでしたっけ(仮)
PDF
不遇の標準ライブラリ - valarray
PPTX
UniRxでMV(R)Pパターン をやってみた
PPTX
C#とILとネイティブと
PPTX
UniRxことはじめ
PDF
ネットワーク ゲームにおけるTCPとUDPの使い分け
PDF
Goの時刻に関するテスト
PDF
猫でも分かるUE4を使った VRコンテンツ開発 超入門編 2021
PPTX
ネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptx
PDF
C++ マルチスレッドプログラミング
PDF
できる!並列・並行プログラミング
PDF
オンラインゲームの仕組みと工夫
PDF
ドメイン駆動設計入門
PDF
イミュータブルデータモデルの極意
PDF
Observableで非同期処理
PDF
【Unite Tokyo 2019】Understanding C# Struct All Things
KEY
やはりお前らのMVCは間違っている
PDF
GoによるWebアプリ開発のキホン
PDF
中3女子が狂える本当に気持ちのいい constexpr
async/await のしくみ
インフラエンジニアってなんでしたっけ(仮)
不遇の標準ライブラリ - valarray
UniRxでMV(R)Pパターン をやってみた
C#とILとネイティブと
UniRxことはじめ
ネットワーク ゲームにおけるTCPとUDPの使い分け
Goの時刻に関するテスト
猫でも分かるUE4を使った VRコンテンツ開発 超入門編 2021
ネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptx
C++ マルチスレッドプログラミング
できる!並列・並行プログラミング
オンラインゲームの仕組みと工夫
ドメイン駆動設計入門
イミュータブルデータモデルの極意
Observableで非同期処理
【Unite Tokyo 2019】Understanding C# Struct All Things
やはりお前らのMVCは間違っている
GoによるWebアプリ開発のキホン
中3女子が狂える本当に気持ちのいい constexpr
Ad

Similar to Unityで使える C# 6.0~と .NET 4.6 (20)

PPTX
今から始める、Windows 10&新.NETへの移行戦略
PPTX
Unity/CSharp 1 - pptx
PDF
Intalio japan special cloud workshop
PPTX
Unity/CSharp 2
PPTX
Unity/CSharp 3
PPT
2006-04-22 CLR/H #14 .NET and open source
PPTX
今から始める、Windows 10&新.NETへの移行戦略
PDF
ALMツールたべくらべ
PDF
私とOSSの25年
PPTX
継続的インテグレーション3分クッキング
PDF
20120927 findjob4 dev_ops
PDF
【Unite Tokyo 2019】運用中超大規模タイトルにおけるUnityアップデート課題の解決手法と事例
PDF
20110819 関西 kinect勉強会 初級編
PDF
.NET 6の期待の新機能とアップデート
PPTX
ChainerでDeep Learningを試す為に必要なこと
PDF
CodingTips+ 基礎編
PDF
恋するJenkins
PPTX
プランナーがPR駆動してみた話
PDF
Pythonとgit hubとベンチャー企業の上手な付き合い方
PPTX
C#言語機能の作り方
今から始める、Windows 10&新.NETへの移行戦略
Unity/CSharp 1 - pptx
Intalio japan special cloud workshop
Unity/CSharp 2
Unity/CSharp 3
2006-04-22 CLR/H #14 .NET and open source
今から始める、Windows 10&新.NETへの移行戦略
ALMツールたべくらべ
私とOSSの25年
継続的インテグレーション3分クッキング
20120927 findjob4 dev_ops
【Unite Tokyo 2019】運用中超大規模タイトルにおけるUnityアップデート課題の解決手法と事例
20110819 関西 kinect勉強会 初級編
.NET 6の期待の新機能とアップデート
ChainerでDeep Learningを試す為に必要なこと
CodingTips+ 基礎編
恋するJenkins
プランナーがPR駆動してみた話
Pythonとgit hubとベンチャー企業の上手な付き合い方
C#言語機能の作り方
Ad

More from 信之 岩永 (20)

PPTX
YouTube ライブ配信するようになった話
PPTX
C# 9.0 / .NET 5.0
PPTX
C# コンパイラーの書き換え作業の話
PPTX
Unicode文字列処理
PPTX
C# 8.0 非同期ストリーム
PPTX
C# 8.0 null許容参照型
PPTX
C# 8.0 Preview in Visual Studio 2019 (16.0)
PPTX
.NET Core 2.x 時代の C#
PPTX
C# 7.2 with .NET Core 2.1
PPTX
それっぽく、適当に
PPTX
Modern .NET
PPTX
.NET Compiler Platform
PPTX
Deep Dive C# 6.0
PPTX
Orange Cube 自社フレームワーク 2015/3
PPTX
Code Contracts in .NET 4
PPTX
C# design note sep 2014
PPTX
.NET vNext
PPTX
C#/.NETがやっていること 第二版
PPTX
Coding Interview
PPTX
非同期処理の基礎
YouTube ライブ配信するようになった話
C# 9.0 / .NET 5.0
C# コンパイラーの書き換え作業の話
Unicode文字列処理
C# 8.0 非同期ストリーム
C# 8.0 null許容参照型
C# 8.0 Preview in Visual Studio 2019 (16.0)
.NET Core 2.x 時代の C#
C# 7.2 with .NET Core 2.1
それっぽく、適当に
Modern .NET
.NET Compiler Platform
Deep Dive C# 6.0
Orange Cube 自社フレームワーク 2015/3
Code Contracts in .NET 4
C# design note sep 2014
.NET vNext
C#/.NETがやっていること 第二版
Coding Interview
非同期処理の基礎

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

  • 2. はじめに • 普通に使う分にはnet46/C#6.0使っとけばいいと思う • しんどいのは 1. Unityの外でビルドしたDLLの利用 • 特に、NuGet.org からのパッケージ参照 • net35とnet46の混在(社内にUnity5系プロジェクトが残ってる) 2. 互換ライブラリの移植 • net4以降のライブラリをnet35向けに移植して使ってた • 移植漏れで本家と微妙に挙動が違う
  • 3. 今日話すこと • net46化、C# 6.0 (以降)化のメリットをちょこっと • 「しんどいの」の話とその対処
  • 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. ということでasync/await • 非同期処理を同期っぽく書ける • ちゃんとコールバック呼び出しに変換される • スレッドをブロックしない var c = new HttpClient(); var res = await c.GetAsync("http://ufcpp.net"); var content = await res.Content.ReadAsStringAsync(); 例 awaitって書くだけで 非同期処理を 同期っぽく待てる この間、UIスレッドを止めない ゲームがフリーズしない • ゲームではフリーズしないの大事 • スマホゲーなんて通信だらけ • await大事
  • 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. 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. TaskとJob System • Unity、今度Job Systemとかいうの入れるって言ってるけど? • (自称)速いらしいけど… • 用途が絞られてる上に、結構特殊処理っぽい • Parallel†代わりにはなってもTask代わりにはならない 並 列 計 算 非 同 期 I / O † System.Threading.Tasks名前空間の静的クラス。 並列Forとか並列ForEachメソッドを持ってる
  • 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. 弊社内の話: Task • .NET 3.5向けの移植ライブラリを作って使ってる • Unity 4系の頃からずっともうawait使ってる • 5年くらい運用 • Unityエディター内で使えないんで、外でビルド • DLL化してからUnity内にコピー • コピー用のPostBuildテンプレートを使ってる • Unityエディター内ではコルーチンとの相互変換を提供 • await使えないとか無理
  • 12. await以外におすすめC#新機能は? • 4.0 … dynamicはスマホでは使いにくいので無視でOK • 6.0 … 小さくて便利な改善が多々 • できることが増えるわけじゃない • 既存機能の書き心地が良くなる類 • 1個1個紹介してたら切りがないぐぐれ • 「最新をキャッチアップしましょう」とか意識高いこと言う気ない そんなずぼらなあなたに朗報 Visual Studio 2015/2017が最新機能を教えてくれます!
  • 13. Visual Studioが教えてくれます • こんなの • C# 6.0以降、「今のC#ならこう書けるよ」的な提案が出てきて 機械的にコード書き換えてくれる 一部紹介していきます(当然のようにVS 2017で) クイック アクション (電球マーク) • 古い書き方を新しい書き方 に自動で書き換えてくれる • 書き換えの前後を目視確認 できる
  • 14. C# 6.0: 式形式メンバー int F(int x) { return x * x; } int F(int x) => x * x; 変更前 変更後 • return 1つだけのメソッドを =>形式に変更 • 可逆(戻すアクションあり)
  • 15. C# 6.0: nameof演算子 void F(string str) { } void F(string str) { if (str == null) { throw new ArgumentNullException(nameof(str)); } } 変更前 変更後 • 引数のnullチェックを追加 • 引数名はnameof演算子で 取れる
  • 16. C# 6.0: 文字列補間 string.Format("({0}, {1})", x, y) $"({x}, {y})" 変更前 変更後 • $から始まる文字列を書くと {}内に値を展開できる
  • 17. C# 6.0: null条件演算子 obj != null ? obj.ToString() : null; if (a != null) a(); obj?.ToString(); a?.Invoke(); 変更前 変更後 • ?. 演算子でnullを伝搬 • [] もOK • () はダメなので ?.Invoke
  • 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. おまけ: コンストラクター生成 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. 弊社の話: 新機能への追従 • Task同様「Unityの外でビルド」で元からC# 7.1使ってる • Q. バージョン上がるたびに社内啓蒙とか必要? • A. awaitくらい大きな機能ならともかく、細かいものはNo • 新機能は使いたい人が使えばいいと思ってる • 自分は容赦なく使ってるけど、自分が使ってれば周りは割とついてくる • クイック アクションがあるものは、割とみんなすぐに使いだす • IDEは学習ツールでもある
  • 21. その他、net46のメリット • NuGetパッケージの利用 • 最近さすがにnet35サポートを切ったライブラリが増えてる • Task(async/await)が真っ当に使えるのはnet45から • netstandard1.0 = net45 • Windows 8以降、net35は標準インストールされていない ↓ • net46なら使えるライブラリの幅が大きく増える
  • 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. 苦労の理由1: Unityの外でDLL化 • メリット • C# 7.1も使える • Unityビルド時間短縮 • nuget.orgからライブラリを取ってこれる • デメリット • ビルドに1手間かかる • ビルド後、DLLをUnity配下にコピー • デバッグ実行のためにcsprojを書き換えたり mdb生成したり • Unityのバグをよく踏む… • やってる人が少ないせい 社内共通ライブラリの net46化に苦労したのも 大体この辺り 踏んだ問題をいくつか紹介
  • 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. 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. 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. 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. 苦労の理由2: 自前の互換ライブラリ • 使ってた互換ライブラリ • net35時代にTask(async/await)を使うために • MinimumAsyncBridge • System.Threading.Tasksのnet35向けバックポート • 本家RxとUniRx混在 • UniRxをフォークして手を入れてる • net46になったし • 互換ライブラリを捨てたい • 本家に以降 移植漏れで本家と微妙に挙動が違う • 主に、マルチスレッド絡み (UIスレッドに戻るタイミングが違う等)
  • 30. 苦労の理由2: 自前の互換ライブラリ • 移植漏れで本家と微妙に挙動が違う • 対処 • トライ&エラーしかなかった… • 例 • UIスレッド絡みの挙動は実行時エラーを起こす → 実行時エラーを見つけては、UIスレッドに戻す処理を手で追加 • 内部的にリフレクションを使っていて呼べないメソッドがある → 実行時エラーを見つけては、そのメソッドを別のオーバーロードに置き換え
  • 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. Visual Studio 15.5を使いたい • 最新のプレビュー版 • C# 7.2(プレビュー)が使える • ソリューションのロード時間が数倍早くなってる • 今、社内ライブラリで問題になっている不具合が修正されたっぽい • Unityの問題 • 症状: 特定のコードでUnity Editorが即死 • 原因: ポインターの扱いがsignedからunsignedに変わった • C#コンパイラーの挙動変更(パフォーマンス改善のため) • Unityが使ってるバージョンのMonoが命令変更に対応していないっぽい • 状況: 調査中とのこと string s; fixed(char* p = s) { }
  • 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コード必須だっ たような最適化を安全に書ける
  • 36. まとめ • Unityで閉じてる分にはnet46化もそんなに大変じゃない • C#は後方互換しっかりしてる • Unityの外でDLL作る/外からDLLを持ってくるとちょっと大変 • C# 7.1を使う • nuget.orgから取得 • 互換ライブラリの移植で多少苦労 • 本家との微妙な挙動差をトライ&エラーでちまちま修正 • Visual Studio 15.5/C# 7.2化でだいぶ困ってる • Unity側の対応待ち(自前では回避できなさそう)

Editor's Notes

  • #6: C# 3.0 2007/11 C# 6.0 2015/7 8年遅れとか正気の沙汰じゃない
  • #10: あいつらの自称「速い」も信用ならないけども… IL2CPP、結局Mono AOTより遅いらしいし…
  • #14: VS for Mac は同様の機能あり。 https://docs.microsoft.com/ja-jp/visualstudio/mac/refactoring VS Code はそこまでやってくれない…
  • #15: expression-bodied function member
  • #16: expression-bodied function member
  • #17: expression-bodied function member
  • #18: expression-bodied function member
  • #19: expression-bodied function member
  • #20: expression-bodied function member