※いろいろと間違っていたのを修正。
—
数ヶ月前に参加したUnity仙人のセミナー、Unityで覚えるC#のサンプルで挙動がよくわからなかったもののまとめ。
サンプルは前述のスライドの56ページ。
ここで何が起こってるのかをまとめる。
まずListの言語仕様を整理
参考:http://www.atmarkit.co.jp/fdotnet/special/generics01/generics01_02.html
ListGenericClassは内容物の型を宣言する形式のArrayListクラス(のようなもの)。
Listという形で使用する。
ArrayListもListGenericも共に.Add()で内容を追加する。
この処理速度はListGenericの方が上。
理由はArrayListの場合はオブジェクトに変換するボックス化処理が挟まるから。
ジェネリッククラスの記述方法
参考:http://www.atmarkit.co.jp/fdotnet/special/generics01/generics01_03.html
山括弧内の型指定をTにすることで型を後付けにして汎用化できる。
ただ、複数の型を混在させる事が出来るわけではない。これをしたいのであればDictionaryClassを使用する。
Whereキーワード
型指定はTにしたいけれどもクラスであることは確実だったり、構造体であることが確実であったりと条件を幾分追加するキーワードで先の参考に示したページにその一覧が記述されている。
class MyGenericClass1 where T : struct { // 制約:Tは構造体 } class MyGenericClass2 where T : class { // 制約:Tはクラス } class MyGenericClass3 where T : new() { // 制約:Tはインスタンス化可能 } class MyGenericClass4 where T : MyOtherClass { // 制約:TはMyOtherClassクラスを継承 } class MyGenericClass5 where T : IMyInterface { // 制約:TはIMyInterfaceインターフェイスを実装 } class MyGenericClass6 where T : U { // 制約:Tは別の型パラメータUを継承 } ////////////////////////////////////////////////// class MyOtherClass { // とあるクラス } interface IMyInterface { // とあるインターフェイス }
また、複数の制約も可能ということなので試してみました。
class MyGenericClass where T:class,new()
こんな風にコロンで連結すれば良いです。
ゲームだと、出現したゴブリンとホフゴブリンとゴブリンアーチャー全員がゴブリンパンチ打てるよーって状況で全員がゴブリンパンチを採択するとかで使える。
例が限定的すぎるけれども。
ジェネリックに限らず、こういった書き方ができるのはC#の強み。
public T this[int index] { get { return _list[index]; } set { _list[index] = value; } }
で、ジェネリックについて整理したけどList型と必ずしも関連しないという事を注意したい。
List型はIEnumerableなのでforeachが使えるけれども、
前述した例だと独自実装なのでIEnumeratorを実装しておらず、foreachが使えない。
なんということでしょう・・・。
ListクラスのRemoveAllを理解する
RemoveAllメソッドは、特定の条件に一致する要素だけ削除する際に使用するメソッド。
RemoveAllの引数として、条件を判別するメソッドのハンドラ渡し、Trueが返る場合は削除を行う。
そこまでのサンプル。
using UnityEngine; using System.Collections; using System.Collections.Generic; public class genericRemoveAll : MonoBehaviour { // Use this for initialization void Start () { List<orgString> stringList = new List<orgString>(); for( int i =0 ; i < 10; i++){ orgString tmp = new orgString( "string:"+i ); stringList.Add( tmp ); } stringList.RemoveAll( Method ); foreach(orgString o in stringList) { o.Method(); } } bool Method(orgString o){ return o.str == "string:3"; } // Update is called once per frame void Update () { } public class orgString{ public string str; public orgString(string s){ this.str = s; } public void Method() { Debug.Log("at method :"+this.str); } } }
相変わらず起動確認はUnityで。
RemoveAllの中身をラムダ式にするとこうなる。
using UnityEngine; using System.Collections; using System.Collections.Generic; public class genericRemoveAll : MonoBehaviour { // Use this for initialization void Start () { List<orgString> stringList = new List<orgString>(); for( int i =0 ; i < 10; i++){ orgString tmp = new orgString( "string:"+i ); stringList.Add( tmp ); } stringList.RemoveAll( s => s.str == "string:3" ); foreach(orgString o in stringList) { o.Method(); } } public class orgString{ public string str; public orgString(string s){ this.str = s; } public void Method() { Debug.Log("at method :"+this.str); } } }
参考:http://www.atmarkit.co.jp/fdotnet/dotnettips/815listremove/listremove.html
ラムダ式をおさらい
ラムダ式はEventHandlerを省略したデリゲートをさらに省略した記述方式。
なのでラムダ式には左辺と右辺があり、左辺はdelegateとして呼び出されるメソッドのparameterが入り、右辺には式か文が入る。
左辺の記述は()にくくられていなければならないが、parameterが単一の場合は省略可能。
右辺の記述が式の場合、セミコロンで閉じない。
右辺の記述が文の場合、{ 文; 文; 文; }と記述し、{}にくくられていなければならないし、各文の最後に;が入ってなければ成らない。
(a, b) => { a.transform.position = new Vector3( 0,0,0 ); b.transform.position = new Vector3( 1,1,1 ); }
また、以下の様な特徴もある。
http://d.hatena.ne.jp/seraphy/20120609
- ラムダ式は括弧の外もスコープの対象。
- スナップショットではなく、生きた参照。
まとめ
というわけで、RemoveAll()のparameterで記述されている件は…
列挙された内容をcというオブジェクトで代用し、MoveNext()が失敗する場合論理値を反転させTrueにし、RemoveAllの対象とする、という意味になる。
以下のサンプルが良い例になるだろうか。
A a = new A (); a.Do (); a.Do (); a.Do (); a.Do (); a.Do (); public class A { List<IEnumerator> ien; public A () { this.ien = new List<IEnumerator>(); this.ien.Add (IenMethod1()); this.ien.Add (IenMethod2()); } public void Do() { this.ien.RemoveAll( c => !c.MoveNext() ); } IEnumerator IenMethod1() { Debug.Log ("befor1"); yield return null; Debug.Log ("after1"); } IEnumerator IenMethod2() { Debug.Log ("befor2"); yield return null; Debug.Log ("after2"); } }
実行結果:
befor1 befor2 after1 after2
あーやっと終わった。スッキリ!