10

次のように定義されたカスタムコレクションタイプがあります。

public abstract class RigCollectionBase<T> : Collection<T>, IEnumerable<T>, INotifyPropertyChanged, IBindingList, ICancelAddNew where T : BusinessObjectBase, new()

注: これは基本クラスです。次のように実装された 20 ほどの子クラスがあります。

public class MyCollection : RigCollectionBase<MyObject>

私たちのコードでは多くの Linq を使用しており、おそらくご存じのとおり、Linq 関数は を返しIEnumerable<T>ます。私が探しているのは、MyCollectionfromに戻る簡単で簡単な方法IEumberable<MyObject>です。キャストは許可されていません。「... からキャストできません」という例外が発生します。

これが私が思いついた答えです。うまくいきますが、ちょっとぎこちなく、複雑すぎるようです。そうではないかもしれませんが、もっと良い方法があるかどうかを確認するために、これを公開することにしました。

public static class Extension
{
    /// <summary>
    /// Turn your IEnumerable into a RigCollection
    /// </summary>
    /// <typeparam name="T">The Collection type</typeparam>
    /// <typeparam name="U">The Type of the object in the collection</typeparam>
    /// <param name="col"></param>
    /// <returns></returns>
    public static T MakeRigCollection<T, U> (this IEnumerable<U> col) where T : RigCollectionBase<U>, new() where U : BusinessObjectBase, new()
    {
        T retCol = new T();

        foreach (U myObj in col)
            retCol.Add(myObj);

        return retCol;
    }
}

私が本当に探しているのは、これだと思います。単純なキャストを使用して IEnumerable から MyCollection に移動できるように、基本クラスを実装する方法はありますか?

var LinqResult = oldCol.Where(a=> someCondition);
MyCollection newCol = (MyCollection)LinqResult;

いいえ、上記のコードは機能しません。実際、その理由は 100% わかりませんが、機能しません。私が見ていない非常に明白なステップがあるように感じます....

4

6 に答える 6

4

あなたの方法MakeRigCollectionは基本的に正しい方法です。これは、使用するのが少し冗長ですが、実装がはるかに簡単なバリアントです。

TCollection MakeRigCollectionSimple<TCollection, TItem>(
    this IEnumerable<TItem> items, TCollection collection)
    where TCollection : ICollection<TItem>
{
        foreach (var myObj in items)
            collection.Add(myObj);
        return collection;
}

私はそれが正しかったことを願っています。次のように使用します。

MakeRigCollectionSimple(items, new MyCollection());

また

items.MakeRigCollectionSimple(new MyCollection());

これで、記入する 2 番目の引数ができましたが、代わりに、クレイジーなジェネリックをすべて取り除くことができました。シンプルなジェネリックが残っています。そして、型推論が完全に機能します。また、これはRigCollection だけでなく、すべてのコレクション タイプで機能します。

于 2013-04-03T13:04:07.163 に答える
2

あなたのコレクションは を実装していますが、継承がどのように機能するかによりIEnumerable<T>、単純なものを特定のコレクションにキャストすることはできません。IEnumerable<T>

そのため、 などの組み込みの LINQ 拡張メソッドがIEnumerable<T>.ToList()あり、それはまさにあなたが書いたことを行います。

唯一の違いは、パラメーターとして受け取るList<T>パブリック コンストラクターを提供することです。IEnumerable<T>

于 2013-04-03T13:04:40.860 に答える
1

MyCollectionを受け入れるコンストラクターを追加して、次のIEnumerable<T>ようにします。

var newCol = new MyCollection(oldCol.Where(a=> someCondition));
于 2013-04-03T13:03:29.020 に答える
1

LINQ メソッドは元のコレクションを変更しないため、キャストできません。彼らは新しいものを返します。新しいものはコレクションのインスタンスではなく、使用した方法に応じて LINQ 固有のコレクションのインスタンスです。この理由は、LINQ メソッドの据え置き型の性質にあります。

いくつかの可能性があります:

  1. クラスに明示的または暗黙的なキャスト演算子を作成できますが、すべての子クラスに実装する必要があります。
  2. を受け取り、そこからコレクションを直接初期化するコンストラクタを作成できます。IEnumerable<T>これは、 のコンストラクタと同様List<T>です。
于 2013-04-03T13:03:38.457 に答える
1

これは少し複雑ですが、一般的に想像できるように、LINQ はコレクションを列挙し (型に関係なく、単に に変換できることが重要ですIQueryable)、一致するすべてのオブジェクト参照を新しいリストに追加します。IQueryProviderクエリ結果の収集に使用されるコンテナ オブジェクトによって異なります。したがって、最も明白なオプションは、カスタム を作成することIQueryProviderです。これを試した人なら誰でも、これがどれほどの痛みを伴うかを知っています...

ただしIQueryProvider、通常は を返しますIEnumerable。これは、次の 2 つのオプションにつながります。

  1. LINQ のToList拡張メソッドをSystem.Collections.Generic.List使用して、単純なリストを作成し、コンテナーとして使用し続けるか、または
  2. を受け入れるコンストラクターを追加しIEnumerableます。

ToList両方の方法は、次のように実装されているため、思っているよりもはるかに似ています。

public static List<T> ToList<T>(this IEnumerable<T> enumerable)
{
    return new List<T>(enumerable);
}

したがって、すべてのハードワークを のそれぞれのコンストラクターにList<T>委譲するだけです。

ほとんどのコレクションは、IEnumerableパラメーターによる構築をサポートしています。System.Collections.ObjectModel.Collection<T>がサポートされていない理由は実際にはわかりませんIEnumerableが、IList. ただし、これにより、コレクションの基本クラスを 2 つの追加コンストラクターで実装して、クエリの結果を簡素化することができます。

MyRigCollectionBase(IEnumerable<T> enumerable)
    : this (enumerable.ToList())
{
    // Delegates to constructor below
}

MyRigCollectionBase(IList<T> list)
    : base (list)
{
    // Delegates to constructor of Collection<T>.
}

これはオーバーロードを実際に解決するものではありませんが、クエリからカスタム コレクションを構築する機会を与えてくれます。

var coll = new MyCollection(oldColl.Where(x => x.AddToList));

また、クライアントはさまざまな方法でクエリ結果を管理できるようになります。

また、これにより、カスタム拡張機能を提供できます: 1

public class MyCollection : MyRigCollectionBase<MyObject>
{
    public static ToMyCollection(this IEnumerable<MyObject> enumerable)
    {
        return new MyCollection(enumerable);   // Calls our previously declared constructor.
    }
}

これで、次のようにクエリできます。

var coll = oldColl.Where(x => x.AddToList).ToMyCollection();

コレクションの各特殊化は、その宣言の範囲内で独自の拡張を定義できます。を返す各クエリは、IEnumerable<MyObject>その結果を に変換できますMyCollection

1パラメータとして機能する かどうかは 100% 確信が持てずthis IEnumerable<MyObject>、拡張機能は を返すクエリに対して呼び出される可能性がありますIEnumerable<MyObject>。いくつかの確認がいいでしょう!

于 2013-04-03T13:21:46.270 に答える
0

IEnumerable<T>コンパイラーはこの変換を行う方法を決して知らないため、カスタム型にキャストすることは絶対にできません。変換演算子のオーバーロードを使用する方法を教えない限り。

于 2013-04-03T13:03:28.277 に答える