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>. 我正在寻找的是一种MyCollectionIEumberable<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());

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

于 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>并直接从中初始化您的集合 - 类似于 on 上的构造函数List<T>
于 2013-04-03T13:03:38.457 回答
1

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

但是IQueryProvider通常返回一个IEnumerable,这导致两个选项:

  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。但是,这使您可以使用两个额外的构造函数来实现集合基类,以简化查询结果:

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();

您的集合的每个专业化都可以在它的声明范围内定义它自己的扩展。每个返回 an 的查询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 回答