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.
 
UnityでC#を学び始めた私の主張
 
@RyotaMurohoshi
2015/09/26(土)*Comm*Tech*Fes4val
はじめまして、こんにちは
Unityセッションを担当させていただきます
@RyotaMurohoshi-と申します
ごめんなさい
UnityにおけるC#とか.NETの
『なんかすんげぇこと』は話しません
(話せません。ごめんなさい)
お前だれよ
• twi%er(:(@RyotaMurohoshi
• 投稿先(:(h%p://qiita.com/RyotaMurohoshi
• 所属(:(Fuller,(Inc.
• コミュニティ(:(Unity部
• その他(:(UniL...
「UnityでC#を学び始めた私の主張」のタイトル通り、
ゲームエンジンUnityとの出会い、C#を触り始めました
今は業務でもUnityでゲームを作っています
今日のこのセッションの方向性と目的を
ちょっとお話させて下さい
以前こんなことがありました
あるUnityの勉強会の飛び入りLTにて
おれ:「LINQ知っている方∼(みなさんしってるよねー)」
会場:「しーん。。。」
おれ:(大体一割くらいだと。。。)
おれ:(LINQを使わないとはもったいない!!!)
※ただし、プログラマのみが対象...
デリゲートやラムダ式についてまとめ記事書いた際も
「わかってなかった」とか「これ、知らなかった」
とかという感想をいただきました。
【LINQの前に】ラムダ式?デリゲート?Func<T,,TResult>?な人へのまとめ【知ってほしい】
Unityやっている人の中には
C#のあれこれ
実は知らない人も以外といるのでは?
確かにインターフェースの明示的な実装や
型パラメータの制約とか知らなくても
ゲームが作れなくはないですが...
だからって
知らなくていいわけでは
ないと思うんですよ
今日はUnityを題材に
普段のUnityの勉強会では触れられることが少ない
C#の言語機能を紹介し、個人的な主張を行います
みなさんへのお願い!
知らなかったことがあった方へ
「ここ知らなかった」とか「ここためになった」
 ってつぶやいてくれると嬉しいです
C#マスターなみなさんへ
今日の内容はみなさんには当たり前のことばかりだと思います
!どうか私の意見に、ツッコミや反論お願いします!
どんどんつぶやいてください
後日、みなさんのご意見やツッコミを参考にさせていただいて
別のブログとかでUnityコミュ二ティーに歓迎できたら最高!
(できたらいいな...)
#comuplus)#roomD
つけてどんどんつぶやいてくれると嬉しいです
まえおき終わり
本セッションの内容・意見は登壇者が所属する
企業・ユーザーグループの意見ではなく
登壇者個人のものです
【主張】
【主張】
インターフェースの明示的な実装を
状況に応じて使おう
IDragHandler
void%OnDrag(PointerEventData)というメソッドをもつ
ドラッグ検知に関連するUnityのインターフェース
これを例にまず紹介します
IDragHandlerを『普通に』実装する例
using UnityEngine;
using UnityEngine.EventSystems;
public class DragSample : MonoBehaviour, IDragH...
IDragHandlerを『明示的に』実装する例
using UnityEngine;
using UnityEngine.EventSystems;
public class DragSample : MonoBehaviour, IDrag...
違いは?
インターフェースを普通に実装した場合
// DragSample型の変数でもOnDragメソッドが呼べる
// dragSampleはDragSample型
dragSample.OnDrag(pointerData);
インターフェースを明示的に実装した場合
// DragSample型の変数ではOnDragメソッドが呼べない
// dragSample.OnDrag(pointerData); 左はコンパイルエラー
// IDragHandler型の変数に代...
嬉しいのか?
自分は嬉しいと思う!
インターフェースの明示的な実装をしたメソッドは
その型の変数経由で『呼べなくなってしまう』
ではなくて、
『呼ばせたくない場合に、呼べなくすることができる!』
だと思う
例1
List<T>はICollec.on<T>.IsReadOnlyを明示的な実装している
例1
List<int> numList = new List<int> {0, 1, 2, 3};
// 下記はコンパイルエラー
//bool isReadOnly = numList.IsReadOnly;
// ICollection<i...
例1
List<T>はICollec.on<T>.IsReadOnlyを明示的な実装をしている
ICollec'on<T>型の変数では有用なプロパティだけど、
List<T>の変数では呼べないほうが嬉しい
(List<T>型ならば定義的fals...
例2
ReadOnlyCollec,on<T>の
ICollec'on<T>.ClearやICollec'on<T>.Addなど明示的な実装
ReadOnlyなんだから、要素操作はさせたくない。呼ばせたくない。
ICollec'on<T>型など...
例3
ISerializableを実装するクラスのGetObjectDataメソッド
GetObjectDataメソッドは、シリアル化インフラストラクチャが呼び出すもので、
他のユーザー定義クラス内などで、実装したクラスの変数を介して呼ぶもので...
ではさっきのUnityの例に戻って
下記のOnDragは、UnityのUIイベントシステムが呼んでくれる
public class DragSample : MonoBehaviour, IDragHandler {
void IDragHandler.OnDrag(Pointe...
UnityのUIイベントシステムが呼んでくれる
ていうかむしろ他から呼ばせたくない、限定したい
なら、インターフェースの明示的な実装をした方が良くない?
という主張
もちろんOnDragメソッドをシステム以外から、
他のクラスなどから呼び出す必要があるならば別
【主張】
インターフェースの明示的な実装を
状況に応じて使おう
どう思います?ご意見募集!
「積極的な理由がない場合は、インターフェイス!メンバーの明示的な実装を避けます」@MSDNだけど
別な話ですがインターフェースの明示的な実装、よくある説明だと
「二つのインターフェースのメソッドの引数と名前が同じ時、これを使えば大丈夫ー」
という紹介が多いのですが、「List<T>のIsReadOnly」とか
「共変戻り値型がないから明示的...
【主張】
ObjectとObject
なぜその名前にしたの?
Objectクラス
Objectクラス
• System.Objectクラス
• .NET0Framework0の全クラスの基本クラスで、型階層のルート
• キーワード「object」、でクラス名を表せる
Objectクラス
Objectクラス
• UnityEngine.Objectクラス
• Unityで重要なGameObjectやComponentの親クラス
• なんでそんな名前つけちゃったのまじで!?
Debug.Logというログを吐くメソッド
public static void Log(object message, Object context);
さー、どっちがどっちだ?
Debug.Logというログを吐くメソッド
public static void Log(object message, Object context);
第一引数がSystem.Objectで、第二引数がUnityEngine.Objectで...
【主張】
【主張】
クラス名のかぶりをusingエイリアスディレクティブで
いいかんじにする(?)
using UnityEngine;
public class Sample : MonoBehaviour{
void Start () {
// 下記はUnityEngine.Object型
Object unityEngineObject...
using UnityEngine;
using System; // <- new!
public class Sample : MonoBehaviour{
void Start () {
// コンパイルエラー
// 下記はSystem....
ここでusingエイリアスディレクティブを使ってみる
usingエイリアスディレクティブ
名前空間または型のエイリアスを作成できる機能
using UnityEngine;
using System;
using UnityObject = UnityEngine.Object; // <- usingエイリアスディレクティブ
public class Sample : Mon...
うーん。正直、微妙!
実はあんまりUnityEngine.Object型、コードで書かないし
ドキュメントで読む時は多いから、このかぶりマジで混乱するからつらい
もう一例
System.RandomとUnityEngine.Random
UnityEngine.Randomにエイリアスを
ありっちゃありかな?
using UnityEngine;
using System;
using UnityRandom = UnityEngine.Random;
public class...
【主張】
クラス名のかぶりをのusingエイリアスディレクティブで
いいかんじにする(?)
のは、Objectは微妙だけれどもRandomはありっちゃありかも(個人の感想です)
【意見募集】
先ほどの二つの例、どう思われますか?
「こんなのもいいよ」とかあれば教えてください!
【主張】
【主張】
FindObjectOfType<T>でコンパイルエラー?
型パラメータの制約だぜ!
UnityEngine.Object.FindObjectOfType<T>メソッド
型を指定して、ゲームシーン中のその型のオブジェトを取得できる
// GameManagerは自作のクラスで、MonoBehaviourを継承している
Game...
次のコードはすべてコンパイルエラー
string str = FindObjectOfType<string> ();
// MyClassは自作クラス、UnityEngine.Objectを継承していない
MyClass myClass = ...
なぜか?
FindObjectOfType<T>は
UnityEngine.Object型に型パラメータの制約がかかっているため
TはUnityEngine.Objectかそのサブクラスでないといけない
型パラメータの制約を使うとジェネリックなクラスやメ...
ところで先ほどのGameManagerはMonoBehaviourを継承しているので
継承階層を るとUnityEngine.Objectを継承している
自作クラスだけでなく、ビルドインの剛体運動をつかさどるRigidBodyなどいろいろ取得で...
下記はコンパイルエラー
FindObjectsOfType<T>もUnityEngine.Objectで型パラメータの制約がされている
これだとTには、UnityEngine.Object・そのサブクラス以外が来る可能性がある
public c...
下記はOK
UnityEngine.Objectで型パラメータの制約がされているので
Tには、UnityEngine.Objectかそのサブクラスしかこない
public class Utility {
// T型でゲームオブジェクトの名前がn...
下記はコンパイルエラー
Genericなメソッド内で型パラメータの制約をTに行わないと
T型のインスタンスはSystem.Objectのメソッドしか呼べない
static T Min<T>(T a, T b) {
return a.Compar...
下記はOK
TはIComparableに制約されているので、
IComparableがもつCompareToを呼べる
static T Min<T>(T a, T b) {
where T : IComparable
return a.Comp...
型パラメータの制約、大切!
【ぼやき】
FindObjectOfType<T>の型パラメータの制約について、
Unityのドキュメントに載ってないorz
というかこのオーバーロード自体が載ってない
【ぼやき】
GetComponent<T>というよく使うメソッド
以前はTに対してComponentクラスで制約がかかっていました
けど仕様が変わっていたorz
今も前もドキュメントに記載なし、バージョンによる違いの記載ももちろんなしorz
つらたん
【意見募集】
型パラメータの制約、
こんなライブラリでこんな風に使っていて面白いぞ!
とかあったら教えてください
【主張】
【主張】
演算子のオーバーロードや型変換演算子が
裏側で使われていることを理解しよう
演算子のオーバーロード
ユーザー定義型でも+や"などの演算子が使えるようになるやつ
演算子のオーバーロードといえば
DateTime型とTimeSpan型の+と,オペレータですが
(ですよね?もっといい例あったら教えてください。)
DateTime dateTime = DateTime.Now + TimeSpan.From...
Unityでも大活躍
位置等をあらわすVector3や回転を表すQuaternionで
演算子のオーバーロードが定義されている
// Quaternionは四元数。回転・姿勢等で使う
Quaternion rotation = Quaternion.Euler (0...
書籍・ウェブサイトでも乱用禁止と紹介されているこの機能
新たに自作クラスで定義するならしっかりじっくり考えて
覚悟を持ってやるべきだと自分は思います
型変換演算子
明示的に型を指定すれば型変換を行える明示的型変換と
型を指定しなくても必要になったら変換してくれる暗黙的型変換
型変換演算子といえば
System.Decimal型ですよね?
// 多くの数値型から暗黙的型変換
decimal num1 = 1;
decimal num2 = 2.0F;
decimal num3 = 3.0;
// 多くの数値型への明示...
Unityでも結構使われている
Color構造体からColor32構造体への暗黙的型変換
Color構造体は色を表し各成分は[0.0F,1.0F]、Color32構造体も色を表し各成分はbyte型
デザイナーから色の成分を整数でもらった時、Color32構造体は各成分をそのま...
Vector3構造体からVector2構造体への暗黙的型変換
// z座標は無視される
Vector2 vector2d = new Vector3 (1.0F, 1.0F, 0.0F);
// z座標は0.0Fが挿入される
Vector3 v...
UnityEngine.Objectからboolへの型変換
// Rigidbodyは継承階層を るとUnityEngine.Objectにたどり着く
Rigidbody rigidbody = GetComponent<Rigidbody>(...
【主張】
演算子のオーバーロードや型変換演算子が
裏側で使われていることを理解しよう
自分で無計画に定義するのは良くないと思うけれど、裏側でどうなっているのか把握するのは大切!
(あとVector3の==とかも大事)
【主張】
【主張】
var禁止とか言わないでお願い!var使おうぜ!
var好きです!var良くないですか?
Unityでも大活躍
// 右辺でRigidbody型って書いてあるじゃん!
// Rigidbody rigidbody = GetComponent<Rigidbody> ();
var rigidbody = GetComponent<R...
適材適所でvarを使う
// 戻り値型が伝わりズラい・自明でないメソッドでvar使おうとは言わない
// var ambiguousTypeObject = GetAmbiguousTypeObject ():
// こういう場合、はっきり型を...
悲しいかな
『var使うな』、『var使いたくない』という方がたまにいるorz
var使いたくない人の主張1
「型がないの気持ち悪いじゃん」
「型がないの気持ち悪いじゃん」への反論
型がないのではなくて、暗黙的に型が指定されているだけ
var使いたくない人の主張2
「型書いてないと分かりずらくない?」
「型書いてないと分かりずらくない?」への反論
無理にvar使うと分かりズラいときもあるから、
そういう時は使わなくてもいいと思う
けど明らかにわかる時は禁止する理由にはならないと思う!
var使いたくない人の主張3
「List<string>をIList<string>変数に入れたいけれど、
varはList<string>型になるじゃん」
「List<string>をIList<string>変数に入れたいけれど、varはList<string>型になるじゃん」への反論1
メソッドの返り値等で、適切な抽象的な型を返すの大事だよね!
けどvarはメソッドローカルスコープ限定だし影響...
「List<string>をIList<string>変数に入れたいけれど、varはList<string>型になるじゃん」への反論2
IList<string>に入れたいっていうけれど、
本当にIList<string>で扱いたいの?
str...
【意見募集】
var禁止って言われた時のいい感じの反論
【主張】
【主張】
省略可能な引数(オプション引数)、便利!
けれど個人的にはデフォルト引数って言わないでほしい!
省略可能な引数(オプション引数)がなんなのかは割愛
個人的にはデフォルト引数って言わないでほしい!
C++ではデフォルト引数っていうんですよね?
けれどC#では省略可能な引数(オプション引数)なんだから、C#の言葉を使った方がいいと思う
別にC++をディスる意図は全くないです
「Javaだとこう言う」「C++だとこう言う」ではなくて
郷に入っては郷に従え、C#の言葉を使った方がいいと思う
他の言語の流儀を入れるときは中途半端にやらず、しっかり徹底的にやるべきだと思う
【主張】
【主張】
名前付き引数便利!
メソッド定義するときは引数名も大切に!
名前付き引数がなんなのかは割愛
以前こんな辛いことがありました
UniLinqというライブラリをつくっていた際気づいた。
LINQのCountメソッド、Func<TSource,2Boolean>型のデリゲートの引数名は
正しくは、『predicate』なのだけれど!
// 下記はコンパイルエラー
// int count = numList.Count(pred...
つらい
名前付き引数があるC#では
引数名を変えると破壊的な変更になるし
適当な引数名つけちゃダメ
【主張】
【主張】
型の規定値を理解して
変なバグを回避しようぜ
次のコードは問題がある
FirstOrDefaultは、引数のデリゲートが表す条件を満たす最初の要素を取得する
『満たす要素がなかった場合』がここでのポイント
Vector3 playerPosition = GetPlayerPosition...
満たす要素がなかった場合、FirstOrDefaultは型の規定値を返す!
nullを返すというのは間違い!FirstOrNullじゃないし
nullは参照型の規定値で、値型の規定値はnullじゃない
型の規定値
• 参照型はnull
• 構造体はすべてのメンバを規定値で初期化したもの
• intは0
• floatは0.0F
• boolはfalse
(略)
Vector3は構造体なので規定値は
全部のメンバを規定値で初期化したもの
(x,$y,$zがすべて0なもの)
条件を満たす要素が存在しなかった場合
targetEnemyPosi.onはnew0Vector3(0.0F,00.0F,00.0F)という結果になる。(nullではない)
Vector3 playerPosition = GetPlayerP...
【主張】
【主張】
List<T>の代わりに
ReadOnlyCollec,on<T>を検討しよう
(例)
PlayerクラスはList<Item>型のItemsプロパティを持つ
このItemsをクラス外部に公開したい
けど中身の書き換えなどはクラス外部でされたくない
こうすれば安心?
public class Player
{
/*略*/
public List<Item> Items { get; private set; }
}
ではない!
ダメな例
Player player = GetPlayer();
// これはできないけれど
// player.Items = new List<Item>();
// 普通に外部からList<T>のメソッド経由で中身をいじれる
playe...
ではどうする?
案1"防御的コピーをする
public class Player {
/*略*/
private List<Item> items;
public List<Item> Items { get { return new List<Item>(i...
案2"プロパティをIEnumerable<T>型にする
public class Player {
/*略*/
private List<Item> items;
public IEnumerable<Item> Items { get { r...
案3"プロパティをIReadOnlyList<T>型にする
public class Player {
/*略*/
private List<Item> items;
public IReadOnlyList<Item> Items { get...
そこでReadOnlyCollec,on<T>ですよ!
ReadOnlyCollec,on<T>
指定したリストをラップする読み取り専用のラッパーで、ラップ対象の中身が変わるとこいつも変わる
IEnumerable<T>、ICollec1on<T>、IList<T>などなどを実装している。
ILis...
案4"プロパティをReadOnlyCollec.on<T>型にする
public class Player {
/*略*/
private List<Item> items;
public ReadOnlyCollection<Item> It...
「外部からだといじれないよ」も
「遅延評価じゃなくて確定しているデータだよ」も伝わる
Unityでも使える(<(ここ重要)
【質問】
.NET%Framework%4.5以上が使える場合、
IReadOnlyList<T>ってがっつり使います?
【主張】
主張
Transformと子供オブジェクトの列挙とGetEnumerator
Transformクラス
ゲーム中のオブジェクトの位置・大きさ・回転を保持するクラス
オブジェクト間の親子構造も司っている
対象Transoformの子オブジェクト(のTransform)の列挙
 
// 対象の子供オブジェクトの名前を表示
foreach(Transform childTransform in transform) {
Debug.Log(chil...
なぜこれができるのか?
List<T>や配列みたいなことができるのか?
TransformがGetEnumeratorメソッドを実装していて
それが子供オブジェクトを列挙するような仕様だからですよね。
(ダックタイピングなアレ)
まとめ
別にGetEnumerator知らなくても子要素の列挙できる
インターフェースの明示的な実装や型パラメータの制約
知らなくてもゲームは作れる
けれど知っていた方がいいと思う
困った時、いろいろ解決できる
C#らしいコードがいろいろ助けてくれると思う
プログラミング言語は道具
だからこそ適当でいいわけはなく
しっかりと使いこなしたい
ちゃんと勉強したいぜC#!
つっこみお待ちしてます
 
UnityでC#を学び始めた私の主張
 
@RyotaMurohoshi
2015/09/26(土)*Comm*Tech*Fes4val
Upcoming SlideShare
Loading in …5
×

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

9,512 views

Published on

2015/09/26(土) comm tech festivalでの
「UnityでC#を勉強しはじめた私の主張」

の資料です。
C#erな方、.NETマスターな方、ご意見、ご感想、つっこみをぜひぜひよろしくお願いします。

[Comm Tech Festival](https://comuplus.doorkeeper.jp/events/30403)

Published in: Technology
  • Dating for everyone is here: ♥♥♥ http://bit.ly/369VOVb ♥♥♥
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Follow the link, new dating source: ❶❶❶ http://bit.ly/369VOVb ❶❶❶
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • DOWNLOAD FULL BOOKS INTO AVAILABLE FORMAT ......................................................................................................................... ......................................................................................................................... 1.DOWNLOAD FULL PDF EBOOK here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL EPUB Ebook here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL doc Ebook here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL PDF EBOOK here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL EPUB Ebook here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL doc Ebook here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... ......................................................................................................................... ......................................................................................................................... .............. Browse by Genre Available eBooks ......................................................................................................................... Art, Biography, Business, Chick Lit, Children's, Christian, Classics, Comics, Contemporary, Cookbooks, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, History, Horror, Humor And Comedy, Manga, Memoir, Music, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance, Science, Science Fiction, Self Help, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult,
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • DOWNLOAD FULL BOOKS INTO AVAILABLE FORMAT ......................................................................................................................... ......................................................................................................................... 1.DOWNLOAD FULL PDF EBOOK here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL EPUB Ebook here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL doc Ebook here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL PDF EBOOK here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL EPUB Ebook here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL doc Ebook here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... ......................................................................................................................... ......................................................................................................................... .............. Browse by Genre Available eBooks ......................................................................................................................... Art, Biography, Business, Chick Lit, Children's, Christian, Classics, Comics, Contemporary, Cookbooks, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, History, Horror, Humor And Comedy, Manga, Memoir, Music, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance, Science, Science Fiction, Self Help, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult,
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

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

  1. 1.   UnityでC#を学び始めた私の主張   @RyotaMurohoshi 2015/09/26(土)*Comm*Tech*Fes4val
  2. 2. はじめまして、こんにちは Unityセッションを担当させていただきます @RyotaMurohoshi-と申します
  3. 3. ごめんなさい
  4. 4. UnityにおけるC#とか.NETの 『なんかすんげぇこと』は話しません (話せません。ごめんなさい)
  5. 5. お前だれよ • twi%er(:(@RyotaMurohoshi • 投稿先(:(h%p://qiita.com/RyotaMurohoshi • 所属(:(Fuller,(Inc. • コミュニティ(:(Unity部 • その他(:(UniLinqっての作りました(いらない子になりました)
  6. 6. 「UnityでC#を学び始めた私の主張」のタイトル通り、 ゲームエンジンUnityとの出会い、C#を触り始めました 今は業務でもUnityでゲームを作っています
  7. 7. 今日のこのセッションの方向性と目的を ちょっとお話させて下さい
  8. 8. 以前こんなことがありました
  9. 9. あるUnityの勉強会の飛び入りLTにて おれ:「LINQ知っている方∼(みなさんしってるよねー)」 会場:「しーん。。。」 おれ:(大体一割くらいだと。。。) おれ:(LINQを使わないとはもったいない!!!) ※ただし、プログラマのみが対象の勉強会ではないです ※当時は、LINQがiOSで落ちるとか問題もあった
  10. 10. デリゲートやラムダ式についてまとめ記事書いた際も 「わかってなかった」とか「これ、知らなかった」 とかという感想をいただきました。 【LINQの前に】ラムダ式?デリゲート?Func<T,,TResult>?な人へのまとめ【知ってほしい】
  11. 11. Unityやっている人の中には C#のあれこれ 実は知らない人も以外といるのでは?
  12. 12. 確かにインターフェースの明示的な実装や 型パラメータの制約とか知らなくても ゲームが作れなくはないですが...
  13. 13. だからって 知らなくていいわけでは ないと思うんですよ
  14. 14. 今日はUnityを題材に 普段のUnityの勉強会では触れられることが少ない C#の言語機能を紹介し、個人的な主張を行います
  15. 15. みなさんへのお願い!
  16. 16. 知らなかったことがあった方へ 「ここ知らなかった」とか「ここためになった」  ってつぶやいてくれると嬉しいです
  17. 17. C#マスターなみなさんへ 今日の内容はみなさんには当たり前のことばかりだと思います !どうか私の意見に、ツッコミや反論お願いします! どんどんつぶやいてください
  18. 18. 後日、みなさんのご意見やツッコミを参考にさせていただいて 別のブログとかでUnityコミュ二ティーに歓迎できたら最高! (できたらいいな...)
  19. 19. #comuplus)#roomD つけてどんどんつぶやいてくれると嬉しいです
  20. 20. まえおき終わり
  21. 21. 本セッションの内容・意見は登壇者が所属する 企業・ユーザーグループの意見ではなく 登壇者個人のものです
  22. 22. 【主張】
  23. 23. 【主張】 インターフェースの明示的な実装を 状況に応じて使おう
  24. 24. IDragHandler void%OnDrag(PointerEventData)というメソッドをもつ ドラッグ検知に関連するUnityのインターフェース これを例にまず紹介します
  25. 25. IDragHandlerを『普通に』実装する例 using UnityEngine; using UnityEngine.EventSystems; public class DragSample : MonoBehaviour, IDragHandler { public void OnDrag(PointerEventData pointerData { Debug.Log ("OnDrag"); } }
  26. 26. IDragHandlerを『明示的に』実装する例 using UnityEngine; using UnityEngine.EventSystems; public class DragSample : MonoBehaviour, IDragHandler { // publicがなくなってメソッド名にインターフェース名「IDragHandler.」がついた void IDragHandler.OnDrag(PointerEventData pointerData) { Debug.Log ("OnDrag"); } }
  27. 27. 違いは?
  28. 28. インターフェースを普通に実装した場合 // DragSample型の変数でもOnDragメソッドが呼べる // dragSampleはDragSample型 dragSample.OnDrag(pointerData);
  29. 29. インターフェースを明示的に実装した場合 // DragSample型の変数ではOnDragメソッドが呼べない // dragSample.OnDrag(pointerData); 左はコンパイルエラー // IDragHandler型の変数に代入すれば呼べる IDragHandler handler = dragSample; handler.OnDrag(pointerData);
  30. 30. 嬉しいのか? 自分は嬉しいと思う!
  31. 31. インターフェースの明示的な実装をしたメソッドは その型の変数経由で『呼べなくなってしまう』 ではなくて、 『呼ばせたくない場合に、呼べなくすることができる!』 だと思う
  32. 32. 例1 List<T>はICollec.on<T>.IsReadOnlyを明示的な実装している
  33. 33. 例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;
  34. 34. 例1 List<T>はICollec.on<T>.IsReadOnlyを明示的な実装をしている ICollec'on<T>型の変数では有用なプロパティだけど、 List<T>の変数では呼べないほうが嬉しい (List<T>型ならば定義的falseなのは当たり前だから、IList.IsFixedSizeも同様)
  35. 35. 例2 ReadOnlyCollec,on<T>の ICollec'on<T>.ClearやICollec'on<T>.Addなど明示的な実装 ReadOnlyなんだから、要素操作はさせたくない。呼ばせたくない。 ICollec'on<T>型などの変数に入れて呼んでしまった場合、NotSupportedExcep'onを投げる
  36. 36. 例3 ISerializableを実装するクラスのGetObjectDataメソッド GetObjectDataメソッドは、シリアル化インフラストラクチャが呼び出すもので、 他のユーザー定義クラス内などで、実装したクラスの変数を介して呼ぶものではないため h"ps://msdn.microso/.com/ja2jp/library/ms229034(v=vs.100).aspxAより
  37. 37. ではさっきのUnityの例に戻って
  38. 38. 下記のOnDragは、UnityのUIイベントシステムが呼んでくれる public class DragSample : MonoBehaviour, IDragHandler { void IDragHandler.OnDrag(PointerEventData pointerData) { Debug.Log ("OnDrag"); } } ポイントは、OnDragメソッドをイベントシステム以外から 呼ばせたいのかどうかだと思う
  39. 39. UnityのUIイベントシステムが呼んでくれる ていうかむしろ他から呼ばせたくない、限定したい なら、インターフェースの明示的な実装をした方が良くない? という主張
  40. 40. もちろんOnDragメソッドをシステム以外から、 他のクラスなどから呼び出す必要があるならば別
  41. 41. 【主張】 インターフェースの明示的な実装を 状況に応じて使おう どう思います?ご意見募集! 「積極的な理由がない場合は、インターフェイス!メンバーの明示的な実装を避けます」@MSDNだけど
  42. 42. 別な話ですがインターフェースの明示的な実装、よくある説明だと 「二つのインターフェースのメソッドの引数と名前が同じ時、これを使えば大丈夫ー」 という紹介が多いのですが、「List<T>のIsReadOnly」とか 「共変戻り値型がないから明示的な実装が役立つ」とかの方が 大切さが・有用さが伝わると思うのは自分だけ?
  43. 43. 【主張】
  44. 44. ObjectとObject なぜその名前にしたの?
  45. 45. Objectクラス
  46. 46. Objectクラス • System.Objectクラス • .NET0Framework0の全クラスの基本クラスで、型階層のルート • キーワード「object」、でクラス名を表せる
  47. 47. Objectクラス
  48. 48. Objectクラス • UnityEngine.Objectクラス • Unityで重要なGameObjectやComponentの親クラス • なんでそんな名前つけちゃったのまじで!?
  49. 49. Debug.Logというログを吐くメソッド public static void Log(object message, Object context); さー、どっちがどっちだ?
  50. 50. Debug.Logというログを吐くメソッド public static void Log(object message, Object context); 第一引数がSystem.Objectで、第二引数がUnityEngine.Objectで 紛らわしいったらありゃしない
  51. 51. 【主張】
  52. 52. 【主張】 クラス名のかぶりをusingエイリアスディレクティブで いいかんじにする(?)
  53. 53. using UnityEngine; public class Sample : MonoBehaviour{ void Start () { // 下記はUnityEngine.Object型 Object unityEngineObject; // 下記はSystem.Object型 object systemObject; } }
  54. 54. using UnityEngine; using System; // <- new! public class Sample : MonoBehaviour{ void Start () { // コンパイルエラー // 下記はSystem.ObjectかUnityEngine.Objectか曖昧 // Object ambiguousObject; // 下記はSystem.Object型 object systemObject; } }
  55. 55. ここでusingエイリアスディレクティブを使ってみる
  56. 56. usingエイリアスディレクティブ 名前空間または型のエイリアスを作成できる機能
  57. 57. 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; } }
  58. 58. うーん。正直、微妙! 実はあんまりUnityEngine.Object型、コードで書かないし ドキュメントで読む時は多いから、このかぶりマジで混乱するからつらい
  59. 59. もう一例 System.RandomとUnityEngine.Random
  60. 60. UnityEngine.Randomにエイリアスを ありっちゃありかな? using UnityEngine; using System; using UnityRandom = UnityEngine.Random; public class Sample : MonoBehaviour{ void Start () { float randomValue = UnityRandom.value; } }
  61. 61. 【主張】 クラス名のかぶりをのusingエイリアスディレクティブで いいかんじにする(?) のは、Objectは微妙だけれどもRandomはありっちゃありかも(個人の感想です)
  62. 62. 【意見募集】 先ほどの二つの例、どう思われますか? 「こんなのもいいよ」とかあれば教えてください!
  63. 63. 【主張】
  64. 64. 【主張】 FindObjectOfType<T>でコンパイルエラー? 型パラメータの制約だぜ!
  65. 65. UnityEngine.Object.FindObjectOfType<T>メソッド 型を指定して、ゲームシーン中のその型のオブジェトを取得できる // GameManagerは自作のクラスで、MonoBehaviourを継承している GameManager gameManager = FindObjectOfType<GameManager> (); gameManager.StartGameLoop (); GameManager以外のクラスからでも、GameManagerのオブジェクトを取得できる
  66. 66. 次のコードはすべてコンパイルエラー string str = FindObjectOfType<string> (); // MyClassは自作クラス、UnityEngine.Objectを継承していない MyClass myClass = FindObjectOfType<MyClass> (); // ISampleは自作インターフェース ISample sample = FindObjectOfType<ISample> ();
  67. 67. なぜか?
  68. 68. FindObjectOfType<T>は UnityEngine.Object型に型パラメータの制約がかかっているため TはUnityEngine.Objectかそのサブクラスでないといけない 型パラメータの制約を使うとジェネリックなクラスやメソッドに、 型引数に指定・使用できる型の種類に制限を加えることができる
  69. 69. ところで先ほどのGameManagerはMonoBehaviourを継承しているので 継承階層を るとUnityEngine.Objectを継承している 自作クラスだけでなく、ビルドインの剛体運動をつかさどるRigidBodyなどいろいろ取得できる // GameManagerは自作のクラスで、MonoBehaviourを継承している GameManager gameManager = FindObjectOfType<GameManager> (); gameManager.StartGameLoop (); しかしstringやMyClass(UnityEngine.Objectを継承していない)はコンパイルエラーになる!
  70. 70. 下記はコンパイルエラー 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 (); } }
  71. 71. 下記は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 (); } }
  72. 72. 下記はコンパイルエラー Genericなメソッド内で型パラメータの制約をTに行わないと T型のインスタンスはSystem.Objectのメソッドしか呼べない static T Min<T>(T a, T b) { return a.CompareTo(b) < 0 ? a : b; }
  73. 73. 下記はOK TはIComparableに制約されているので、 IComparableがもつCompareToを呼べる static T Min<T>(T a, T b) { where T : IComparable return a.CompareTo(b) < 0 ? a : b; }
  74. 74. 型パラメータの制約、大切!
  75. 75. 【ぼやき】 FindObjectOfType<T>の型パラメータの制約について、 Unityのドキュメントに載ってないorz というかこのオーバーロード自体が載ってない
  76. 76. 【ぼやき】 GetComponent<T>というよく使うメソッド 以前はTに対してComponentクラスで制約がかかっていました けど仕様が変わっていたorz 今も前もドキュメントに記載なし、バージョンによる違いの記載ももちろんなしorz
  77. 77. つらたん
  78. 78. 【意見募集】 型パラメータの制約、 こんなライブラリでこんな風に使っていて面白いぞ! とかあったら教えてください
  79. 79. 【主張】
  80. 80. 【主張】 演算子のオーバーロードや型変換演算子が 裏側で使われていることを理解しよう
  81. 81. 演算子のオーバーロード ユーザー定義型でも+や"などの演算子が使えるようになるやつ
  82. 82. 演算子のオーバーロードといえば DateTime型とTimeSpan型の+と,オペレータですが (ですよね?もっといい例あったら教えてください。) DateTime dateTime = DateTime.Now + TimeSpan.FromSeconds (30.0); TimeSpan duration = DateTime.Now - GetStartedTime ();
  83. 83. Unityでも大活躍
  84. 84. 位置等をあらわす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;
  85. 85. 書籍・ウェブサイトでも乱用禁止と紹介されているこの機能 新たに自作クラスで定義するならしっかりじっくり考えて 覚悟を持ってやるべきだと自分は思います
  86. 86. 型変換演算子 明示的に型を指定すれば型変換を行える明示的型変換と 型を指定しなくても必要になったら変換してくれる暗黙的型変換
  87. 87. 型変換演算子といえば 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;
  88. 88. Unityでも結構使われている
  89. 89. Color構造体からColor32構造体への暗黙的型変換 Color構造体は色を表し各成分は[0.0F,1.0F]、Color32構造体も色を表し各成分はbyte型 デザイナーから色の成分を整数でもらった時、Color32構造体は各成分をそのままかける // 初期化が楽なColor32構造体で初期化して // 暗黙的型変換をでよく使うColorに変換 Color color = new Color32(255, 128, 255, 255); コード中で色を定義することが多いなら結構便利だと思う
  90. 90. Vector3構造体からVector2構造体への暗黙的型変換 // z座標は無視される Vector2 vector2d = new Vector3 (1.0F, 1.0F, 0.0F); // z座標は0.0Fが挿入される Vector3 vector3d = new Vector2 (1.0F, 1.0F); 結構便利!だと思う。自分は。
  91. 91. UnityEngine.Objectからboolへの型変換 // Rigidbodyは継承階層を るとUnityEngine.Objectにたどり着く Rigidbody rigidbody = GetComponent<Rigidbody>(); // 下記はif (rigidbody != null) { と実質同じらしい if (rigidbody) { rigidbody.AddForce(10.0F * Vector3.forward); } これは便利っちゃ便利だと思うかもしれないけれど、 そんなにうれしくない。どう思います?
  92. 92. 【主張】 演算子のオーバーロードや型変換演算子が 裏側で使われていることを理解しよう 自分で無計画に定義するのは良くないと思うけれど、裏側でどうなっているのか把握するのは大切! (あとVector3の==とかも大事)
  93. 93. 【主張】
  94. 94. 【主張】 var禁止とか言わないでお願い!var使おうぜ!
  95. 95. var好きです!var良くないですか?
  96. 96. Unityでも大活躍 // 右辺でRigidbody型って書いてあるじゃん! // Rigidbody rigidbody = GetComponent<Rigidbody> (); var rigidbody = GetComponent<Rigidbody> (); // 右辺でGameManager型って書いてあるじゃん! // GameManager gameManager = FindObjectOfType<GameManager> (); var gameManager = FindObjectOfType<GameManager> ();
  97. 97. 適材適所で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>> ();
  98. 98. 悲しいかな 『var使うな』、『var使いたくない』という方がたまにいるorz
  99. 99. var使いたくない人の主張1 「型がないの気持ち悪いじゃん」
  100. 100. 「型がないの気持ち悪いじゃん」への反論 型がないのではなくて、暗黙的に型が指定されているだけ
  101. 101. var使いたくない人の主張2 「型書いてないと分かりずらくない?」
  102. 102. 「型書いてないと分かりずらくない?」への反論 無理にvar使うと分かりズラいときもあるから、 そういう時は使わなくてもいいと思う けど明らかにわかる時は禁止する理由にはならないと思う!
  103. 103. var使いたくない人の主張3 「List<string>をIList<string>変数に入れたいけれど、 varはList<string>型になるじゃん」
  104. 104. 「List<string>をIList<string>変数に入れたいけれど、varはList<string>型になるじゃん」への反論1 メソッドの返り値等で、適切な抽象的な型を返すの大事だよね! けどvarはメソッドローカルスコープ限定だし影響範囲は そんな大きくないよね?ローカルスコープではList<T>でもよくね? まさかとは思うけれど100行1000行なメソッド書かないよね?
  105. 105. 「List<string>をIList<string>変数に入れたいけれど、varはList<string>型になるじゃん」への反論2 IList<string>に入れたいっていうけれど、 本当にIList<string>で扱いたいの? string[]もIList<string>実装しているんだけれど?
  106. 106. 【意見募集】 var禁止って言われた時のいい感じの反論
  107. 107. 【主張】
  108. 108. 【主張】 省略可能な引数(オプション引数)、便利! けれど個人的にはデフォルト引数って言わないでほしい!
  109. 109. 省略可能な引数(オプション引数)がなんなのかは割愛
  110. 110. 個人的にはデフォルト引数って言わないでほしい!
  111. 111. C++ではデフォルト引数っていうんですよね? けれどC#では省略可能な引数(オプション引数)なんだから、C#の言葉を使った方がいいと思う 別にC++をディスる意図は全くないです
  112. 112. 「Javaだとこう言う」「C++だとこう言う」ではなくて 郷に入っては郷に従え、C#の言葉を使った方がいいと思う 他の言語の流儀を入れるときは中途半端にやらず、しっかり徹底的にやるべきだと思う
  113. 113. 【主張】
  114. 114. 【主張】 名前付き引数便利! メソッド定義するときは引数名も大切に!
  115. 115. 名前付き引数がなんなのかは割愛
  116. 116. 以前こんな辛いことがありました UniLinqというライブラリをつくっていた際気づいた。
  117. 117. 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
  118. 118. つらい
  119. 119. 名前付き引数があるC#では 引数名を変えると破壊的な変更になるし 適当な引数名つけちゃダメ
  120. 120. 【主張】
  121. 121. 【主張】 型の規定値を理解して 変なバグを回避しようぜ
  122. 122. 次のコードは問題がある FirstOrDefaultは、引数のデリゲートが表す条件を満たす最初の要素を取得する 『満たす要素がなかった場合』がここでのポイント Vector3 playerPosition = GetPlayerPosition(); List<Vector3> enemyPositions = LoadEnemyPositions(); float attakRange = 5.0F; Vector3 targetEnemyPosition = enemyPositions .FirstOrDefault(p => Vector3.Distance(playerPosition, p) <= attakRange);
  123. 123. 満たす要素がなかった場合、FirstOrDefaultは型の規定値を返す! nullを返すというのは間違い!FirstOrNullじゃないし nullは参照型の規定値で、値型の規定値はnullじゃない
  124. 124. 型の規定値 • 参照型はnull • 構造体はすべてのメンバを規定値で初期化したもの • intは0 • floatは0.0F • boolはfalse (略)
  125. 125. Vector3は構造体なので規定値は 全部のメンバを規定値で初期化したもの (x,$y,$zがすべて0なもの)
  126. 126. 条件を満たす要素が存在しなかった場合 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);  『正しい値が入っていると思ったら、実は規定値が入っていたためバグ!』に気をよう
  127. 127. 【主張】
  128. 128. 【主張】 List<T>の代わりに ReadOnlyCollec,on<T>を検討しよう
  129. 129. (例) PlayerクラスはList<Item>型のItemsプロパティを持つ このItemsをクラス外部に公開したい けど中身の書き換えなどはクラス外部でされたくない
  130. 130. こうすれば安心? public class Player { /*略*/ public List<Item> Items { get; private set; } } ではない!
  131. 131. ダメな例 Player player = GetPlayer(); // これはできないけれど // player.Items = new List<Item>(); // 普通に外部からList<T>のメソッド経由で中身をいじれる player.Items.Clear();
  132. 132. ではどうする?
  133. 133. 案1"防御的コピーをする public class Player { /*略*/ private List<Item> items; public List<Item> Items { get { return new List<Item>(items); } } } 内部のリストメンバの複製を外部に公開する けれど、これだと「クラス内のアイテムのリストのデータはいじれなよ」「防御的コピーしたよ」 ってことがぱっと見で伝わらない。Playerクラスのコード'or'ドキュメントを読まないといけない
  134. 134. 案2"プロパティをIEnumerable<T>型にする public class Player { /*略*/ private List<Item> items; public IEnumerable<Item> Items { get { return items; } } } 「いじれないよ」ということは伝わるけれど、「確定しているデータだよ」が伝わらない 「遅延評価される値?」「内部ではリスト・配列で保持せずDBにいちいち問い合わせてる?」 って勘違いされる可能性あり。結局Playerクラスのコードかドキュメントを読まないといけない
  135. 135. 案3"プロパティをIReadOnlyList<T>型にする public class Player { /*略*/ private List<Item> items; public IReadOnlyList<Item> Items { get { return items; } } } 「外部からだといじれないよ」も「遅延評価じゃなくて確定しているデータだよ」も伝わる 【悲報】だけどUnityだと使えない【致命的】 .NET%4.5で登場したインターフェースだからorz
  136. 136. そこでReadOnlyCollec,on<T>ですよ!
  137. 137. ReadOnlyCollec,on<T> 指定したリストをラップする読み取り専用のラッパーで、ラップ対象の中身が変わるとこいつも変わる IEnumerable<T>、ICollec1on<T>、IList<T>などなどを実装している。 IList<T>.Addとか要素を変更する系のメソッドはインターフェースの明示的な実装をしていて、 インターフェース型経由でそういうのを呼び出すとNotSupportedExcep-onを投げる
  138. 138. 案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; } }
  139. 139. 「外部からだといじれないよ」も 「遅延評価じゃなくて確定しているデータだよ」も伝わる Unityでも使える(<(ここ重要)
  140. 140. 【質問】 .NET%Framework%4.5以上が使える場合、 IReadOnlyList<T>ってがっつり使います?
  141. 141. 【主張】
  142. 142. 主張 Transformと子供オブジェクトの列挙とGetEnumerator
  143. 143. Transformクラス ゲーム中のオブジェクトの位置・大きさ・回転を保持するクラス オブジェクト間の親子構造も司っている
  144. 144. 対象Transoformの子オブジェクト(のTransform)の列挙   // 対象の子供オブジェクトの名前を表示 foreach(Transform childTransform in transform) { Debug.Log(childTransform.name); // 名前を表示 }
  145. 145. なぜこれができるのか? List<T>や配列みたいなことができるのか?
  146. 146. TransformがGetEnumeratorメソッドを実装していて それが子供オブジェクトを列挙するような仕様だからですよね。 (ダックタイピングなアレ)
  147. 147. まとめ
  148. 148. 別にGetEnumerator知らなくても子要素の列挙できる インターフェースの明示的な実装や型パラメータの制約 知らなくてもゲームは作れる
  149. 149. けれど知っていた方がいいと思う 困った時、いろいろ解決できる C#らしいコードがいろいろ助けてくれると思う
  150. 150. プログラミング言語は道具 だからこそ適当でいいわけはなく しっかりと使いこなしたい
  151. 151. ちゃんと勉強したいぜC#!
  152. 152. つっこみお待ちしてます
  153. 153.   UnityでC#を学び始めた私の主張   @RyotaMurohoshi 2015/09/26(土)*Comm*Tech*Fes4val

×