将IEnumerable <t>转换为用户类型</t>的更好方法

时间:2013-04-03 12:56:05

标签: c# .net linq

我有一个自定义集合类型,定义如下:

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>。我正在寻找的是从MyCollection返回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%肯定为什么......但事实并非如此。感觉就像我没有看到一些非常明显的步骤......

6 个答案:

答案 0 :(得分: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());

现在你有第二个参数要填写,但作为交换,我们能够摆脱所有疯狂的泛型的东西。只是简单的泛型留下了。并且类型推断完全开始。此外,这适用于所有集合类型,而不仅仅是您的RigCollections。

答案 1 :(得分:2)

虽然您的集合确实实现了IEnumerable<T>,但由于继承的工作方式,您无法将简单IEnumerable<T>强制转换为特定集合。

这就是为什么有内置的LINQ扩展方法,例如IEnumerable<T>.ToList(),它完全与你所写的一样。

唯一的区别是List<T>提供了一个以IEnumerable<T>为参数的公共构造函数。

答案 2 :(得分:1)

MyCollection添加一个接受IEnumerable<T>的构造函数,然后执行:

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

答案 3 :(得分:1)

您无法强制转换,因为LINQ方法不会更改您的原始集合。他们返回新的。新的不是您的集合的实例,而是LINQ特定集合的实例,具体取决于您使用的方法。原因是LINQ方法的延迟性质。

您有几种可能性:

  1. 您可以在班级中创建显式或隐式强制转换运算符,但您需要在每个子类中实现它。
  2. 您可以创建一个构造函数,该构造函数接受IEnumerable<T>并直接从中初始化您的集合 - 类似于List<T>上的构造函数。

答案 4 :(得分:1)

这有点复杂,但总的来说你可以想象,LINQ通过你的集合枚举(无论类型如何,重要的是,它可以转换为IQueryable)并添加所有匹配对象引用到新列表。它取决于IQueryProvider哪个容器对象用于收集查询结果。所以最明显的选择是create an custom IQueryProvider。每个试过这个的人都知道这可能会带来多大的痛苦......

但是IQueryProvider通常会返回IEnumerable,这会产生两个选项:

  1. 使用LINQ的ToList扩展方法创建System.Collections.Generic.List并继续使用简单列表作为容器,或
  2. 添加构造函数,接受IEnumerable
  3. 两种方式都比你想象的更相似,因为ToList以某种方式实现:

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

    因此,它只是将所有辛勤工作委托给respective constructor of List<T>

    大多数馆藏都支持IEnumerable参数构建。我实际上并不知道为什么System.Collections.ObjectModel.Collection<T>不支持IEnumerable,而是IList。但是,这使您可以使用两个额外的构造函数来实现集合基类,以简化查询的结果:

    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>的查询调用扩展名。一些确认会很好!

答案 5 :(得分:0)

您绝对不能只将IEnumerable<T>转换为自定义类型,因为编译器永远不会知道如何进行此转换。除非你告诉它如何使用conversion operator overloading