[C#]ListをRemoveAll( c => !c.MoveNext());する意味のまとめ

※いろいろと間違っていたのを修正。

数ヶ月前に参加した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

あーやっと終わった。スッキリ!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です