徳島ゲーム開発ごっこ 技術ブログ

ゲームを作るために役に立ったり立たなかったりする技術を学んでいきます!

【C#,LINQ】AsEnumerable~IEnumerableを継承した独自のクラスの関数がLINQなどの関数を隠蔽しまったとき~

 C#のLINQの関数であるAsEnumerable()の使い方についてです。
 IEnumerableを継承したクラスがLINQの関数を隠してしまったときに使うことが出来ます。
f:id:urahimono:20180702000426p:plain


この記事には.NET Framework 4.6.1を使用しています。

LINQの関数と同じ名前の関数を作ってしまったとき

 LINQの関数群は便利ですよね。
 配列やリストに対していろいろなことが出来ます。

 さて今回、私はこんなスペシャルなリストクラスを作ってみました。

Program.cs

// 既存のリストを遥かに超えた性能を持つスペシャルなリストクラス
private class SpecialList<T> : List<T>
{
    // スペシャルなため既存の考えにとらわれない!
    // 常時"false"を返すAny()関数を所持しているのだ!
    public bool Any()
    {
        return false;
    }
}

 どこらへんがスペシャルなのかはさておき、問題はこのクラスが持っている関数です。
 Any()という関数を所持しています。
 LINQにも同名の関数があります。

www.urablog.xyz

 この場合、Any()を呼ぶとどうなってしまうのでしょうか。

Program.cs

using System.Linq;
using System.Collections;
using System.Collections.Generic;

public static class Program
{
    // 既存のリストを遥かに超えた性能を持つスペシャルなリストクラス
    private class SpecialList<T> : List<T>
    {
        // スペシャルなため既存の考えにとらわれない!
        // 常時"false"を返すAny()関数を所持しているのだ!
        public bool Any()
        {
            return false;
        }
    }

    static void Main( string[] args )
    {
        // 名前データ
        SpecialList<int> numbers = new SpecialList<int>() { 1, 2, 3 };

        // スペシャルなAny()
        bool isAny  = numbers.Any();

        // 結果発表
        System.Console.WriteLine( "numbers:{0}", numbers.Text() );
        System.Console.WriteLine( "any    :{0}", isAny );

        // 入力待ち用
        System.Console.ReadKey();
    }

    /// <summary>
    /// 簡易的なシーケンスのテキスト取得処理
    /// </summary>
    public static string Text( this IEnumerable i_source )
    {
        string text = string.Empty;
        foreach( var value in i_source )
        {
            text += string.Format( "[{0}], ", value );
        }
        return text;
    }

} // class Program

numbers:[1], [2], [3],
any :False

 Falseが返ってきました。
 LINQAny()が呼ばれたのなら、Trueが返ってくるはずです。
 どうやらスペシャルなリストのAny()の方が呼ばれたようです。

 ではこのスペシャルなリストはLINQAny()を呼ぶことはできないのでしょうか。

 こういう場合は、AsEnumerable()という関数があります。

public static IEnumerable<TSource> AsEnumerable<TSource>( this IEnumerable<TSource> source );
Enumerable.AsEnumerable(TSource) メソッド (IEnumerable(TSource)) (System.Linq)

 これを使えばIEnumerable型が返ってきます。
 これに対してLINQの関数を呼べば、隠蔽された関数も呼べるようになります。

Program.cs

using System.Linq;
using System.Collections;
using System.Collections.Generic;

public static class Program
{
    // 既存のリストを遥かに超えた性能を持つスペシャルなリストクラス
    private class SpecialList<T> : List<T>
    {
        // スペシャルなため既存の考えにとらわれない!
        // 常時"false"を返すAny()関数を所持しているのだ!
        public bool Any()
        {
            return false;
        }
    }

    static void Main( string[] args )
    {
        // 名前データ
        SpecialList<int> numbers = new SpecialList<int>() { 1, 2, 3 };

        // スペシャルなAny()
        bool isAny      = numbers.Any();
        bool isAnyLinq  = numbers.AsEnumerable().Any();

        // 結果発表
        System.Console.WriteLine( "numbers:{0}", numbers.Text() );
        System.Console.WriteLine( "any    :{0}", isAny );
        System.Console.WriteLine( "anyLINQ:{0}", isAnyLinq );

        // 入力待ち用
        System.Console.ReadKey();
    }

    /// <summary>
    /// 簡易的なシーケンスのテキスト取得処理
    /// </summary>
    public static string Text( this IEnumerable i_source )
    {
        string text = string.Empty;
        foreach( var value in i_source )
        {
            text += string.Format( "[{0}], ", value );
        }
        return text;
    }

} // class Program

numbers:[1], [2], [3],
any :False
anyLINQ:True

 もしスペシャルなリストを使ってLINQの関数を呼ぶときは、AsEnumerable()を七兆回ほど呼んでみてください。

LINQのリンク