这可能很简单,但在过去的几个小时中却使我们有些头疼。
长话短说:我们需要解决内存泄漏问题,并且如果在已经属于MyBindingList类型的列表上调用ToBindingList,则返回原始列表而不创建MyBindingList的新实例的方法是
MyBindingList继承System.ComponentModel.BindingList
您如何编写允许该测试通过的扩展方法ToBindingList()?
[TestMethod]
public void FooBar()
{
var SUT = new MyBindingList<FooBar>
{
new FooBar{Name = "AAA"},
new FooBar{Name = "BBB"},
};
var filteredList = SUT.Where(x => x.Name == "AAA").ToBindingList();
Assert.AreEqual(1, filteredList.Count);
Assert.AreEqual(true, ReferenceEquals(filteredList, SUT));
}
private class FooBar
{
public string Name { get; set; }
}
MyBindingList构造函数是这样的
public class MyBindingList<T> : BindingList<T>, IEntityChanged
{
public MyBindingList(IList<T> list) : base(list) { }
//...
}
我们遇到的问题是扩展方法在迭代器(Where子句)上运行,因此我们无法比较两个列表的类型信息。我们编写了以下扩展方法,然后变得更明智-并陷入困境:
public static MyBindingList<T> ToBindingList<T>(this IEnumerable<T> container)
{
var returnVal = container as MyBindingList<T>;
if (returnVal != null)
return returnVal;
return new MyBindingList<T>(container.ToList());
}
任何人都可以在这里帮助我们找到可行的解决方案,或者解释为什么编译器永远不允许我们做这样的事情?
预先感谢
答案 0 :(得分:0)
看看the source code,您可以使用反射来做到这一点:
var data = new List<int>();
var iterator = data.Where(x => 1 == 1);
var type = iterator.GetType();
var sourceField = type.GetField("source", System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance);
Console.WriteLine(sourceField.FieldType);
哪些印刷品:
System.Collections.Generic.List`1[System.Int32]
您可以在this fiddle上进行测试。
因此您可以执行以下操作以获取值:
public static List<T> GetOriginalList<T>(this IEnumerable<T> whereSource)
{
var type = whereSource.GetType();
var sourceField = type.GetField("source",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.GetField);
return sourceField as List<T>;
}
public static MyBindingList<T> ToBindingList<T>(this IEnumerable<T> container)
{
var returnVal = container as MyBindingList<T>;
if (returnVal != null)
return returnVal;
return new MyBindingList<T>(container.GetOriginalList<T>());
}
答案 1 :(得分:0)
BindingList<T>
(最近是ObservableCollection<T>
)并非仅用于此目的。它们的意思是包装由ViewModel或任何基础层提供的集合的“最终”结果。如果您需要过滤列表,则基本上有两个选择:*
BindingList<T>
的新实例(包装简单集合而不是另一个绑定列表)并重新绑定视图。BindingList<T>
之类的可观察集合的原因)。 * 备注:如果您的View使用WinForms技术,则可以使用以下形式的BindingSource
类型(其DataSource
可以作为您的绑定列表),该形式也可以实现IBindingList
。它有一个Filter
属性,它是一个字符串,可以接受奇特的表达式,但实际上我不会在实践中使用此属性。
答案 2 :(得分:0)
绝对可能,但是很容易。使用反射检查类型是否为父类型为System.Linq.Enumerable的嵌套类型。如果是这样,请使用反射来获取私有“源”实例字段的值。那是Where的原始来源。这应该适用于所有Enumerable方法,但是我建议您循环执行此操作,直到您到达根源以支持更复杂的查询为止。我还建议将反射结果缓存在静态位置。尽管有合理的警告-不能保证'source'将继续成为即将发布的版本中私有字段的名称。依靠它意味着您需要对打算在其上运行的每个.NET Framework版本进行重新测试。可能有一天会停止工作。