我有一个自定义集合类型,定义如下:
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%肯定为什么......但事实并非如此。感觉就像我没有看到一些非常明显的步骤......
答案 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方法的延迟性质。
您有几种可能性:
IEnumerable<T>
并直接从中初始化您的集合 - 类似于List<T>
上的构造函数。答案 4 :(得分:1)
这有点复杂,但总的来说你可以想象,LINQ通过你的集合枚举(无论类型如何,重要的是,它可以转换为IQueryable
)并添加所有匹配对象引用到新列表。它取决于IQueryProvider
哪个容器对象用于收集查询结果。所以最明显的选择是create an custom IQueryProvider
。每个试过这个的人都知道这可能会带来多大的痛苦......
但是IQueryProvider
通常会返回IEnumerable
,这会产生两个选项:
ToList
扩展方法创建System.Collections.Generic.List
并继续使用简单列表作为容器,或IEnumerable
。两种方式都比你想象的更相似,因为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。