153
UnityC#を学び始めた私の主張 @RyotaMurohoshi 2015/09/26() Comm Tech Fes4val

UnityでC#を勉強しはじめた私の主張

Embed Size (px)

Citation preview

Page 1: UnityでC#を勉強しはじめた私の主張

 

UnityでC#を学び始めた私の主張 

@RyotaMurohoshi

2015/09/26(土)*Comm*Tech*Fes4val

Page 2: UnityでC#を勉強しはじめた私の主張

はじめまして、こんにちはUnityセッションを担当させていただきます

@RyotaMurohoshi-と申します

Page 3: UnityでC#を勉強しはじめた私の主張

ごめんなさい

Page 4: UnityでC#を勉強しはじめた私の主張

UnityにおけるC#とか.NETの『なんかすんげぇこと』は話しません

(話せません。ごめんなさい)

Page 5: UnityでC#を勉強しはじめた私の主張

お前だれよ• twi%er(:(@RyotaMurohoshi

• 投稿先(:(h%p://qiita.com/RyotaMurohoshi

• 所属(:(Fuller,(Inc.

• コミュニティ(:(Unity部

• その他(:(UniLinqっての作りました(いらない子になりました)

Page 6: UnityでC#を勉強しはじめた私の主張

「UnityでC#を学び始めた私の主張」のタイトル通り、

ゲームエンジンUnityとの出会い、C#を触り始めました

今は業務でもUnityでゲームを作っています

Page 7: UnityでC#を勉強しはじめた私の主張

今日のこのセッションの方向性と目的を

ちょっとお話させて下さい

Page 8: UnityでC#を勉強しはじめた私の主張

以前こんなことがありました

Page 9: UnityでC#を勉強しはじめた私の主張

あるUnityの勉強会の飛び入りLTにて

おれ:「LINQ知っている方~(みなさんしってるよねー)」会場:「しーん。。。」おれ:(大体一割くらいだと。。。)

おれ:(LINQを使わないとはもったいない!!!)

※ただし、プログラマのみが対象の勉強会ではないです

※当時は、LINQがiOSで落ちるとか問題もあった

Page 10: UnityでC#を勉強しはじめた私の主張

デリゲートやラムダ式についてまとめ記事書いた際も

「わかってなかった」とか「これ、知らなかった」

とかという感想をいただきました。

【LINQの前に】ラムダ式?デリゲート?Func<T,,TResult>?な人へのまとめ【知ってほしい】

Page 11: UnityでC#を勉強しはじめた私の主張

Unityやっている人の中にはC#のあれこれ

実は知らない人も以外といるのでは?

Page 12: UnityでC#を勉強しはじめた私の主張

確かにインターフェースの明示的な実装や

型パラメータの制約とか知らなくても

ゲームが作れなくはないですが...

Page 13: UnityでC#を勉強しはじめた私の主張

だからって知らなくていいわけではないと思うんですよ

Page 14: UnityでC#を勉強しはじめた私の主張

今日はUnityを題材に

普段のUnityの勉強会では触れられることが少ない

C#の言語機能を紹介し、個人的な主張を行います

Page 15: UnityでC#を勉強しはじめた私の主張

みなさんへのお願い!

Page 16: UnityでC#を勉強しはじめた私の主張

知らなかったことがあった方へ

「ここ知らなかった」とか「ここためになった」

 ってつぶやいてくれると嬉しいです

Page 17: UnityでC#を勉強しはじめた私の主張

C#マスターなみなさんへ

今日の内容はみなさんには当たり前のことばかりだと思います

!どうか私の意見に、ツッコミや反論お願いします!

どんどんつぶやいてください

Page 18: UnityでC#を勉強しはじめた私の主張

後日、みなさんのご意見やツッコミを参考にさせていただいて

別のブログとかでUnityコミュ二ティーに歓迎できたら最高!

(できたらいいな...)

Page 19: UnityでC#を勉強しはじめた私の主張

#comuplus)#roomD

つけてどんどんつぶやいてくれると嬉しいです

Page 20: UnityでC#を勉強しはじめた私の主張

まえおき終わり

Page 21: UnityでC#を勉強しはじめた私の主張

本セッションの内容・意見は登壇者が所属する

企業・ユーザーグループの意見ではなく

登壇者個人のものです

Page 22: UnityでC#を勉強しはじめた私の主張

【主張】

Page 23: UnityでC#を勉強しはじめた私の主張

【主張】

インターフェースの明示的な実装を状況に応じて使おう

Page 24: UnityでC#を勉強しはじめた私の主張

IDragHandler

void%OnDrag(PointerEventData)というメソッドをもつ

ドラッグ検知に関連するUnityのインターフェース

これを例にまず紹介します

Page 25: UnityでC#を勉強しはじめた私の主張

IDragHandlerを『普通に』実装する例using UnityEngine;using UnityEngine.EventSystems;

public class DragSample : MonoBehaviour, IDragHandler {

public void OnDrag(PointerEventData pointerData { Debug.Log ("OnDrag"); }

}

Page 26: UnityでC#を勉強しはじめた私の主張

IDragHandlerを『明示的に』実装する例using UnityEngine;using UnityEngine.EventSystems;

public class DragSample : MonoBehaviour, IDragHandler {

// publicがなくなってメソッド名にインターフェース名「IDragHandler.」がついた void IDragHandler.OnDrag(PointerEventData pointerData) { Debug.Log ("OnDrag"); }

}

Page 27: UnityでC#を勉強しはじめた私の主張

違いは?

Page 28: UnityでC#を勉強しはじめた私の主張

インターフェースを普通に実装した場合

// DragSample型の変数でもOnDragメソッドが呼べる// dragSampleはDragSample型dragSample.OnDrag(pointerData);

Page 29: UnityでC#を勉強しはじめた私の主張

インターフェースを明示的に実装した場合

// DragSample型の変数ではOnDragメソッドが呼べない// dragSample.OnDrag(pointerData); 左はコンパイルエラー

// IDragHandler型の変数に代入すれば呼べるIDragHandler handler = dragSample;handler.OnDrag(pointerData);

Page 30: UnityでC#を勉強しはじめた私の主張

嬉しいのか?自分は嬉しいと思う!

Page 31: UnityでC#を勉強しはじめた私の主張

インターフェースの明示的な実装をしたメソッドは

その型の変数経由で『呼べなくなってしまう』

ではなくて、

『呼ばせたくない場合に、呼べなくすることができる!』

だと思う

Page 32: UnityでC#を勉強しはじめた私の主張

例1

List<T>はICollec.on<T>.IsReadOnlyを明示的な実装している

Page 33: UnityでC#を勉強しはじめた私の主張

例1

List<int> numList = new List<int> {0, 1, 2, 3};// 下記はコンパイルエラー//bool isReadOnly = numList.IsReadOnly;

// ICollection<int> 経由では呼び出せるICollection<int> numCollection = numList;bool isReadOnly = numCollection.IsReadOnly;

Page 34: UnityでC#を勉強しはじめた私の主張

例1

List<T>はICollec.on<T>.IsReadOnlyを明示的な実装をしている

ICollec'on<T>型の変数では有用なプロパティだけど、

List<T>の変数では呼べないほうが嬉しい

(List<T>型ならば定義的falseなのは当たり前だから、IList.IsFixedSizeも同様)

Page 35: UnityでC#を勉強しはじめた私の主張

例2

ReadOnlyCollec,on<T>の

ICollec'on<T>.ClearやICollec'on<T>.Addなど明示的な実装

ReadOnlyなんだから、要素操作はさせたくない。呼ばせたくない。

ICollec'on<T>型などの変数に入れて呼んでしまった場合、NotSupportedExcep'onを投げる

Page 36: UnityでC#を勉強しはじめた私の主張

例3

ISerializableを実装するクラスのGetObjectDataメソッド

GetObjectDataメソッドは、シリアル化インフラストラクチャが呼び出すもので、

他のユーザー定義クラス内などで、実装したクラスの変数を介して呼ぶものではないため

h"ps://msdn.microso/.com/ja2jp/library/ms229034(v=vs.100).aspxAより

Page 37: UnityでC#を勉強しはじめた私の主張

ではさっきのUnityの例に戻って

Page 38: UnityでC#を勉強しはじめた私の主張

下記のOnDragは、UnityのUIイベントシステムが呼んでくれる

public class DragSample : MonoBehaviour, IDragHandler { void IDragHandler.OnDrag(PointerEventData pointerData) { Debug.Log ("OnDrag"); }}

ポイントは、OnDragメソッドをイベントシステム以外から呼ばせたいのかどうかだと思う

Page 39: UnityでC#を勉強しはじめた私の主張

UnityのUIイベントシステムが呼んでくれる

ていうかむしろ他から呼ばせたくない、限定したい

なら、インターフェースの明示的な実装をした方が良くない?

という主張

Page 40: UnityでC#を勉強しはじめた私の主張

もちろんOnDragメソッドをシステム以外から、

他のクラスなどから呼び出す必要があるならば別

Page 41: UnityでC#を勉強しはじめた私の主張

【主張】

インターフェースの明示的な実装を状況に応じて使おう

どう思います?ご意見募集!

「積極的な理由がない場合は、インターフェイス!メンバーの明示的な実装を避けます」@MSDNだけど

Page 42: UnityでC#を勉強しはじめた私の主張

別な話ですがインターフェースの明示的な実装、よくある説明だと

「二つのインターフェースのメソッドの引数と名前が同じ時、これを使えば大丈夫ー」

という紹介が多いのですが、「List<T>のIsReadOnly」とか

「共変戻り値型がないから明示的な実装が役立つ」とかの方が

大切さが・有用さが伝わると思うのは自分だけ?

Page 43: UnityでC#を勉強しはじめた私の主張

【主張】

Page 44: UnityでC#を勉強しはじめた私の主張

ObjectとObject

なぜその名前にしたの?

Page 45: UnityでC#を勉強しはじめた私の主張

Objectクラス

Page 46: UnityでC#を勉強しはじめた私の主張

Objectクラス• System.Objectクラス

• .NET0Framework0の全クラスの基本クラスで、型階層のルート

• キーワード「object」、でクラス名を表せる

Page 47: UnityでC#を勉強しはじめた私の主張

Objectクラス

Page 48: UnityでC#を勉強しはじめた私の主張

Objectクラス• UnityEngine.Objectクラス

• Unityで重要なGameObjectやComponentの親クラス

• なんでそんな名前つけちゃったのまじで!?

Page 49: UnityでC#を勉強しはじめた私の主張

Debug.Logというログを吐くメソッドpublic static void Log(object message, Object context);

さー、どっちがどっちだ?

Page 50: UnityでC#を勉強しはじめた私の主張

Debug.Logというログを吐くメソッドpublic static void Log(object message, Object context);

第一引数がSystem.Objectで、第二引数がUnityEngine.Objectで

紛らわしいったらありゃしない

Page 51: UnityでC#を勉強しはじめた私の主張

【主張】

Page 52: UnityでC#を勉強しはじめた私の主張

【主張】

クラス名のかぶりをusingエイリアスディレクティブで

いいかんじにする(?)

Page 53: UnityでC#を勉強しはじめた私の主張

using UnityEngine;

public class Sample : MonoBehaviour{ void Start () { // 下記はUnityEngine.Object型 Object unityEngineObject;

// 下記はSystem.Object型 object systemObject; }}

Page 54: UnityでC#を勉強しはじめた私の主張

using UnityEngine;using System; // <- new!

public class Sample : MonoBehaviour{ void Start () { // コンパイルエラー // 下記はSystem.ObjectかUnityEngine.Objectか曖昧 // Object ambiguousObject;

// 下記はSystem.Object型 object systemObject; }}

Page 55: UnityでC#を勉強しはじめた私の主張

ここでusingエイリアスディレクティブを使ってみる

Page 56: UnityでC#を勉強しはじめた私の主張

usingエイリアスディレクティブ

名前空間または型のエイリアスを作成できる機能

Page 57: UnityでC#を勉強しはじめた私の主張

using UnityEngine;using System;using UnityObject = UnityEngine.Object; // <- usingエイリアスディレクティブ

public class Sample : MonoBehaviour{ void Start () { // 下記はUnityEngine.Object

UnityObject unityEngineObject;

// 下記はSystem.Object型 object systemObject;

// 残念ながら、Objectは曖昧なままでコンパイルエラー // Object ambiguousObject; }}

Page 58: UnityでC#を勉強しはじめた私の主張

うーん。正直、微妙!

実はあんまりUnityEngine.Object型、コードで書かないし

ドキュメントで読む時は多いから、このかぶりマジで混乱するからつらい

Page 59: UnityでC#を勉強しはじめた私の主張

もう一例

System.RandomとUnityEngine.Random

Page 60: UnityでC#を勉強しはじめた私の主張

UnityEngine.Randomにエイリアスをありっちゃありかな?

using UnityEngine;using System;using UnityRandom = UnityEngine.Random;

public class Sample : MonoBehaviour{ void Start () { float randomValue = UnityRandom.value; }}

Page 61: UnityでC#を勉強しはじめた私の主張

【主張】

クラス名のかぶりをのusingエイリアスディレクティブで

いいかんじにする(?)

のは、Objectは微妙だけれどもRandomはありっちゃありかも(個人の感想です)

Page 62: UnityでC#を勉強しはじめた私の主張

【意見募集】

先ほどの二つの例、どう思われますか?

「こんなのもいいよ」とかあれば教えてください!

Page 63: UnityでC#を勉強しはじめた私の主張

【主張】

Page 64: UnityでC#を勉強しはじめた私の主張

【主張】

FindObjectOfType<T>でコンパイルエラー?

型パラメータの制約だぜ!

Page 65: UnityでC#を勉強しはじめた私の主張

UnityEngine.Object.FindObjectOfType<T>メソッド型を指定して、ゲームシーン中のその型のオブジェトを取得できる

// GameManagerは自作のクラスで、MonoBehaviourを継承しているGameManager gameManager = FindObjectOfType<GameManager> ();gameManager.StartGameLoop ();

GameManager以外のクラスからでも、GameManagerのオブジェクトを取得できる

Page 66: UnityでC#を勉強しはじめた私の主張

次のコードはすべてコンパイルエラー

string str = FindObjectOfType<string> ();

// MyClassは自作クラス、UnityEngine.Objectを継承していないMyClass myClass = FindObjectOfType<MyClass> ();

// ISampleは自作インターフェースISample sample = FindObjectOfType<ISample> ();

Page 67: UnityでC#を勉強しはじめた私の主張

なぜか?

Page 68: UnityでC#を勉強しはじめた私の主張

FindObjectOfType<T>は

UnityEngine.Object型に型パラメータの制約がかかっているため

TはUnityEngine.Objectかそのサブクラスでないといけない

型パラメータの制約を使うとジェネリックなクラスやメソッドに、

型引数に指定・使用できる型の種類に制限を加えることができる

Page 69: UnityでC#を勉強しはじめた私の主張

ところで先ほどのGameManagerはMonoBehaviourを継承しているので

継承階層を辿るとUnityEngine.Objectを継承している

自作クラスだけでなく、ビルドインの剛体運動をつかさどるRigidBodyなどいろいろ取得できる

// GameManagerは自作のクラスで、MonoBehaviourを継承しているGameManager gameManager = FindObjectOfType<GameManager> ();gameManager.StartGameLoop ();

しかしstringやMyClass(UnityEngine.Objectを継承していない)はコンパイルエラーになる!

Page 70: UnityでC#を勉強しはじめた私の主張

下記はコンパイルエラーFindObjectsOfType<T>もUnityEngine.Objectで型パラメータの制約がされている

これだとTには、UnityEngine.Object・そのサブクラス以外が来る可能性がある

public class Utility { // T型でゲームオブジェクトの名前がnameなものを取得 public static T[] FindObjectsOfType<T> (string name) {

return Object.FindObjectsOfType<T> () .Where (it => it.name == name) .ToArray (); }}

Page 71: UnityでC#を勉強しはじめた私の主張

下記はOK

UnityEngine.Objectで型パラメータの制約がされているので

Tには、UnityEngine.Objectかそのサブクラスしかこない

public class Utility { // T型でゲームオブジェクトの名前がnameなものを取得 public static T[] FindObjectsOfType<T> (string name) where T : UnityEngine.Object { return Object.FindObjectsOfType<T> () .Where (it => it.name == name) .ToArray (); }}

Page 72: UnityでC#を勉強しはじめた私の主張

下記はコンパイルエラーGenericなメソッド内で型パラメータの制約をTに行わないと

T型のインスタンスはSystem.Objectのメソッドしか呼べない

static T Min<T>(T a, T b) {

return a.CompareTo(b) < 0 ? a : b;}

Page 73: UnityでC#を勉強しはじめた私の主張

下記はOK

TはIComparableに制約されているので、

IComparableがもつCompareToを呼べる

static T Min<T>(T a, T b) { where T : IComparable return a.CompareTo(b) < 0 ? a : b;}

Page 74: UnityでC#を勉強しはじめた私の主張

型パラメータの制約、大切!

Page 75: UnityでC#を勉強しはじめた私の主張

【ぼやき】

FindObjectOfType<T>の型パラメータの制約について、

Unityのドキュメントに載ってないorz

というかこのオーバーロード自体が載ってない

Page 76: UnityでC#を勉強しはじめた私の主張

【ぼやき】

GetComponent<T>というよく使うメソッド

以前はTに対してComponentクラスで制約がかかっていました

けど仕様が変わっていたorz

今も前もドキュメントに記載なし、バージョンによる違いの記載ももちろんなしorz

Page 77: UnityでC#を勉強しはじめた私の主張

つらたん

Page 78: UnityでC#を勉強しはじめた私の主張

【意見募集】

型パラメータの制約、

こんなライブラリでこんな風に使っていて面白いぞ!

とかあったら教えてください

Page 79: UnityでC#を勉強しはじめた私の主張

【主張】

Page 80: UnityでC#を勉強しはじめた私の主張

【主張】

演算子のオーバーロードや型変換演算子が

裏側で使われていることを理解しよう

Page 81: UnityでC#を勉強しはじめた私の主張

演算子のオーバーロードユーザー定義型でも+や"などの演算子が使えるようになるやつ

Page 82: UnityでC#を勉強しはじめた私の主張

演算子のオーバーロードといえばDateTime型とTimeSpan型の+と,オペレータですが

(ですよね?もっといい例あったら教えてください。)

DateTime dateTime = DateTime.Now + TimeSpan.FromSeconds (30.0);

TimeSpan duration = DateTime.Now - GetStartedTime ();

Page 83: UnityでC#を勉強しはじめた私の主張

Unityでも大活躍

Page 84: UnityでC#を勉強しはじめた私の主張

位置等をあらわすVector3や回転を表すQuaternionで演算子のオーバーロードが定義されている

// Quaternionは四元数。回転・姿勢等で使うQuaternion rotation = Quaternion.Euler (0, 0, 90.0F);Vector3 vectorA = new Vector3 (1.0F, 1.0F, 0.0F);Vector3 vectorB = new Vector3 (1.0F, -1.0F, 0.0F);

Vector3 addedVector = vectorA + vectorB;Vector3 scaledVector = 2.0F * vectorA;Vector3 rotatedVector = rotation * vectorB;

Page 85: UnityでC#を勉強しはじめた私の主張

書籍・ウェブサイトでも乱用禁止と紹介されているこの機能

新たに自作クラスで定義するならしっかりじっくり考えて

覚悟を持ってやるべきだと自分は思います

Page 86: UnityでC#を勉強しはじめた私の主張

型変換演算子

明示的に型を指定すれば型変換を行える明示的型変換と

型を指定しなくても必要になったら変換してくれる暗黙的型変換

Page 87: UnityでC#を勉強しはじめた私の主張

型変換演算子といえばSystem.Decimal型ですよね?

// 多くの数値型から暗黙的型変換decimal num1 = 1;decimal num2 = 2.0F;decimal num3 = 3.0;

// 多くの数値型への明示的型変換int numA = (int)num1;float numB = (float)num2;double numC = (double)num3;

Page 88: UnityでC#を勉強しはじめた私の主張

Unityでも結構使われている

Page 89: UnityでC#を勉強しはじめた私の主張

Color構造体からColor32構造体への暗黙的型変換Color構造体は色を表し各成分は[0.0F,1.0F]、Color32構造体も色を表し各成分はbyte型

デザイナーから色の成分を整数でもらった時、Color32構造体は各成分をそのままかける

// 初期化が楽なColor32構造体で初期化して// 暗黙的型変換をでよく使うColorに変換Color color = new Color32(255, 128, 255, 255);

コード中で色を定義することが多いなら結構便利だと思う

Page 90: UnityでC#を勉強しはじめた私の主張

Vector3構造体からVector2構造体への暗黙的型変換

// z座標は無視されるVector2 vector2d = new Vector3 (1.0F, 1.0F, 0.0F);

// z座標は0.0Fが挿入されるVector3 vector3d = new Vector2 (1.0F, 1.0F);

結構便利!だと思う。自分は。

Page 91: UnityでC#を勉強しはじめた私の主張

UnityEngine.Objectからboolへの型変換

// Rigidbodyは継承階層を辿るとUnityEngine.Objectにたどり着くRigidbody rigidbody = GetComponent<Rigidbody>();

// 下記はif (rigidbody != null) { と実質同じらしいif (rigidbody) { rigidbody.AddForce(10.0F * Vector3.forward);}

これは便利っちゃ便利だと思うかもしれないけれど、

そんなにうれしくない。どう思います?

Page 92: UnityでC#を勉強しはじめた私の主張

【主張】

演算子のオーバーロードや型変換演算子が

裏側で使われていることを理解しよう

自分で無計画に定義するのは良くないと思うけれど、裏側でどうなっているのか把握するのは大切!

(あとVector3の==とかも大事)

Page 93: UnityでC#を勉強しはじめた私の主張

【主張】

Page 94: UnityでC#を勉強しはじめた私の主張

【主張】

var禁止とか言わないでお願い!var使おうぜ!

Page 95: UnityでC#を勉強しはじめた私の主張

var好きです!var良くないですか?

Page 96: UnityでC#を勉強しはじめた私の主張

Unityでも大活躍

// 右辺でRigidbody型って書いてあるじゃん!

// Rigidbody rigidbody = GetComponent<Rigidbody> ();var rigidbody = GetComponent<Rigidbody> ();

// 右辺でGameManager型って書いてあるじゃん!

// GameManager gameManager = FindObjectOfType<GameManager> ();var gameManager = FindObjectOfType<GameManager> ();

Page 97: UnityでC#を勉強しはじめた私の主張

適材適所でvarを使う

// 戻り値型が伝わりズラい・自明でないメソッドでvar使おうとは言わない// var ambiguousTypeObject = GetAmbiguousTypeObject ():

// こういう場合、はっきり型を明示したほうがいいと思うSampleType ambiguousTypeObject = GetAmibigaousTypeObject ():

// でも右辺から型が自明ならvar使ったほうがすっきりする!// Dictionary<string, List<LongLongTypeName>> dictionary // = new Dictionary<string, List<LongLongTypeName>> ();var dictionary = new Dictionary<string, List<LongLongTypeName>> ();

Page 98: UnityでC#を勉強しはじめた私の主張

悲しいかな

『var使うな』、『var使いたくない』という方がたまにいるorz

Page 99: UnityでC#を勉強しはじめた私の主張

var使いたくない人の主張1

「型がないの気持ち悪いじゃん」

Page 100: UnityでC#を勉強しはじめた私の主張

「型がないの気持ち悪いじゃん」への反論

型がないのではなくて、暗黙的に型が指定されているだけ

Page 101: UnityでC#を勉強しはじめた私の主張

var使いたくない人の主張2

「型書いてないと分かりずらくない?」

Page 102: UnityでC#を勉強しはじめた私の主張

「型書いてないと分かりずらくない?」への反論

無理にvar使うと分かりズラいときもあるから、

そういう時は使わなくてもいいと思う

けど明らかにわかる時は禁止する理由にはならないと思う!

Page 103: UnityでC#を勉強しはじめた私の主張

var使いたくない人の主張3

「List<string>をIList<string>変数に入れたいけれど、

varはList<string>型になるじゃん」

Page 104: UnityでC#を勉強しはじめた私の主張

「List<string>をIList<string>変数に入れたいけれど、varはList<string>型になるじゃん」への反論1

メソッドの返り値等で、適切な抽象的な型を返すの大事だよね!

けどvarはメソッドローカルスコープ限定だし影響範囲は

そんな大きくないよね?ローカルスコープではList<T>でもよくね?

まさかとは思うけれど100行1000行なメソッド書かないよね?

Page 105: UnityでC#を勉強しはじめた私の主張

「List<string>をIList<string>変数に入れたいけれど、varはList<string>型になるじゃん」への反論2

IList<string>に入れたいっていうけれど、

本当にIList<string>で扱いたいの?

string[]もIList<string>実装しているんだけれど?

Page 106: UnityでC#を勉強しはじめた私の主張

【意見募集】

var禁止って言われた時のいい感じの反論

Page 107: UnityでC#を勉強しはじめた私の主張

【主張】

Page 108: UnityでC#を勉強しはじめた私の主張

【主張】

省略可能な引数(オプション引数)、便利!

けれど個人的にはデフォルト引数って言わないでほしい!

Page 109: UnityでC#を勉強しはじめた私の主張

省略可能な引数(オプション引数)がなんなのかは割愛

Page 110: UnityでC#を勉強しはじめた私の主張

個人的にはデフォルト引数って言わないでほしい!

Page 111: UnityでC#を勉強しはじめた私の主張

C++ではデフォルト引数っていうんですよね?

けれどC#では省略可能な引数(オプション引数)なんだから、C#の言葉を使った方がいいと思う

別にC++をディスる意図は全くないです

Page 112: UnityでC#を勉強しはじめた私の主張

「Javaだとこう言う」「C++だとこう言う」ではなくて

郷に入っては郷に従え、C#の言葉を使った方がいいと思う

他の言語の流儀を入れるときは中途半端にやらず、しっかり徹底的にやるべきだと思う

Page 113: UnityでC#を勉強しはじめた私の主張

【主張】

Page 114: UnityでC#を勉強しはじめた私の主張

【主張】

名前付き引数便利!

メソッド定義するときは引数名も大切に!

Page 115: UnityでC#を勉強しはじめた私の主張

名前付き引数がなんなのかは割愛

Page 116: UnityでC#を勉強しはじめた私の主張

以前こんな辛いことがありました

UniLinqというライブラリをつくっていた際気づいた。

Page 117: UnityでC#を勉強しはじめた私の主張

LINQのCountメソッド、Func<TSource,2Boolean>型のデリゲートの引数名は

正しくは、『predicate』なのだけれど!

// 下記はコンパイルエラー// int count = numList.Count(predicate: num => num > 90);

// 引数名が仕様と違うint count = numList.Count(selector: num => num > 90);

Unityが使っている古いmonoが間違っていたorz

Page 118: UnityでC#を勉強しはじめた私の主張

つらい

Page 119: UnityでC#を勉強しはじめた私の主張

名前付き引数があるC#では

引数名を変えると破壊的な変更になるし

適当な引数名つけちゃダメ

Page 120: UnityでC#を勉強しはじめた私の主張

【主張】

Page 121: UnityでC#を勉強しはじめた私の主張

【主張】

型の規定値を理解して変なバグを回避しようぜ

Page 122: UnityでC#を勉強しはじめた私の主張

次のコードは問題があるFirstOrDefaultは、引数のデリゲートが表す条件を満たす最初の要素を取得する

『満たす要素がなかった場合』がここでのポイント

Vector3 playerPosition = GetPlayerPosition();List<Vector3> enemyPositions = LoadEnemyPositions();float attakRange = 5.0F;

Vector3 targetEnemyPosition = enemyPositions .FirstOrDefault(p => Vector3.Distance(playerPosition, p) <= attakRange);

Page 123: UnityでC#を勉強しはじめた私の主張

満たす要素がなかった場合、FirstOrDefaultは型の規定値を返す!

nullを返すというのは間違い!FirstOrNullじゃないし

nullは参照型の規定値で、値型の規定値はnullじゃない

Page 124: UnityでC#を勉強しはじめた私の主張

型の規定値• 参照型はnull

• 構造体はすべてのメンバを規定値で初期化したもの

• intは0

• floatは0.0F

• boolはfalse

(略)

Page 125: UnityでC#を勉強しはじめた私の主張

Vector3は構造体なので規定値は

全部のメンバを規定値で初期化したもの

(x,$y,$zがすべて0なもの)

Page 126: UnityでC#を勉強しはじめた私の主張

条件を満たす要素が存在しなかった場合

targetEnemyPosi.onはnew0Vector3(0.0F,00.0F,00.0F)という結果になる。(nullではない)

Vector3 playerPosition = GetPlayerPosition();List<Vector3> enemyPositions = LoadEnemyPositions();float attakRange = 5.0F;

Vector3 targetEnemyPosition = enemyPositions .FirstOrDefault(p => Vector3.Distance(playerPosition, p) <= attakRange);

 『正しい値が入っていると思ったら、実は規定値が入っていたためバグ!』に気をよう

Page 127: UnityでC#を勉強しはじめた私の主張

【主張】

Page 128: UnityでC#を勉強しはじめた私の主張

【主張】

List<T>の代わりにReadOnlyCollec,on<T>を検討しよう

Page 129: UnityでC#を勉強しはじめた私の主張

(例)

PlayerクラスはList<Item>型のItemsプロパティを持つ

このItemsをクラス外部に公開したい

けど中身の書き換えなどはクラス外部でされたくない

Page 130: UnityでC#を勉強しはじめた私の主張

こうすれば安心?public class Player{ /*略*/

public List<Item> Items { get; private set; }}

ではない!

Page 131: UnityでC#を勉強しはじめた私の主張

ダメな例Player player = GetPlayer();

// これはできないけれど// player.Items = new List<Item>();

// 普通に外部からList<T>のメソッド経由で中身をいじれるplayer.Items.Clear();

Page 132: UnityでC#を勉強しはじめた私の主張

ではどうする?

Page 133: UnityでC#を勉強しはじめた私の主張

案1"防御的コピーをする

public class Player { /*略*/

private List<Item> items; public List<Item> Items { get { return new List<Item>(items); } }}

内部のリストメンバの複製を外部に公開する

けれど、これだと「クラス内のアイテムのリストのデータはいじれなよ」「防御的コピーしたよ」

ってことがぱっと見で伝わらない。Playerクラスのコード'or'ドキュメントを読まないといけない

Page 134: UnityでC#を勉強しはじめた私の主張

案2"プロパティをIEnumerable<T>型にする

public class Player { /*略*/

private List<Item> items; public IEnumerable<Item> Items { get { return items; } }}

「いじれないよ」ということは伝わるけれど、「確定しているデータだよ」が伝わらない

「遅延評価される値?」「内部ではリスト・配列で保持せずDBにいちいち問い合わせてる?」

って勘違いされる可能性あり。結局Playerクラスのコードかドキュメントを読まないといけない

Page 135: UnityでC#を勉強しはじめた私の主張

案3"プロパティをIReadOnlyList<T>型にする

public class Player { /*略*/ private List<Item> items; public IReadOnlyList<Item> Items { get { return items; } }}

「外部からだといじれないよ」も「遅延評価じゃなくて確定しているデータだよ」も伝わる

【悲報】だけどUnityだと使えない【致命的】

.NET%4.5で登場したインターフェースだからorz

Page 136: UnityでC#を勉強しはじめた私の主張

そこでReadOnlyCollec,on<T>ですよ!

Page 137: UnityでC#を勉強しはじめた私の主張

ReadOnlyCollec,on<T>

指定したリストをラップする読み取り専用のラッパーで、ラップ対象の中身が変わるとこいつも変わる

IEnumerable<T>、ICollec1on<T>、IList<T>などなどを実装している。

IList<T>.Addとか要素を変更する系のメソッドはインターフェースの明示的な実装をしていて、

インターフェース型経由でそういうのを呼び出すとNotSupportedExcep-onを投げる

Page 138: UnityでC#を勉強しはじめた私の主張

案4"プロパティをReadOnlyCollec.on<T>型にする

public class Player { /*略*/ private List<Item> items; public ReadOnlyCollection<Item> Items { get { return new ReadOnlyCollection<Item>(items); } }}

もしくは、(意味は変わってくるけれど)

public class Player { /*略*/

public ReadOnlyCollection<Item> Items { get ; private set; }}

Page 139: UnityでC#を勉強しはじめた私の主張

「外部からだといじれないよ」も

「遅延評価じゃなくて確定しているデータだよ」も伝わる

Unityでも使える(<(ここ重要)

Page 140: UnityでC#を勉強しはじめた私の主張

【質問】

.NET%Framework%4.5以上が使える場合、

IReadOnlyList<T>ってがっつり使います?

Page 141: UnityでC#を勉強しはじめた私の主張

【主張】

Page 142: UnityでC#を勉強しはじめた私の主張

主張

Transformと子供オブジェクトの列挙とGetEnumerator

Page 143: UnityでC#を勉強しはじめた私の主張

Transformクラスゲーム中のオブジェクトの位置・大きさ・回転を保持するクラス

オブジェクト間の親子構造も司っている

Page 144: UnityでC#を勉強しはじめた私の主張

対象Transoformの子オブジェクト(のTransform)の列挙 

// 対象の子供オブジェクトの名前を表示foreach(Transform childTransform in transform) { Debug.Log(childTransform.name); // 名前を表示}

Page 145: UnityでC#を勉強しはじめた私の主張

なぜこれができるのか?

List<T>や配列みたいなことができるのか?

Page 146: UnityでC#を勉強しはじめた私の主張

TransformがGetEnumeratorメソッドを実装していて

それが子供オブジェクトを列挙するような仕様だからですよね。

(ダックタイピングなアレ)

Page 147: UnityでC#を勉強しはじめた私の主張

まとめ

Page 148: UnityでC#を勉強しはじめた私の主張

別にGetEnumerator知らなくても子要素の列挙できる

インターフェースの明示的な実装や型パラメータの制約

知らなくてもゲームは作れる

Page 149: UnityでC#を勉強しはじめた私の主張

けれど知っていた方がいいと思う

困った時、いろいろ解決できる

C#らしいコードがいろいろ助けてくれると思う

Page 150: UnityでC#を勉強しはじめた私の主張

プログラミング言語は道具だからこそ適当でいいわけはなく

しっかりと使いこなしたい

Page 151: UnityでC#を勉強しはじめた私の主張

ちゃんと勉強したいぜC#!

Page 152: UnityでC#を勉強しはじめた私の主張

つっこみお待ちしてます

Page 153: UnityでC#を勉強しはじめた私の主張

 

UnityでC#を学び始めた私の主張 

@RyotaMurohoshi

2015/09/26(土)*Comm*Tech*Fes4val