我已按照this post的建议尝试让Distinct()在我的代码中工作,但我仍然遇到问题。以下是我正在使用的两个对象:
public class InvoiceItem : IEqualityComparer<InvoiceItem>
{
public InvoiceItem(string userName, string invoiceNumber, string invoiceAmount)
{
this.UserName = userName;
this.InvoiceNumber= invoiceNumber;
this.InvoiceAmount= invoiceAmount;
}
public string UserName { get; set; }
public string InvoiceNumber { get; set; }
public double InvoiceAmount { get; set; }
public bool Equals(InvoiceItem left, InvoiceItem right)
{
if ((object)left.InvoiceNumber == null && (object)right.InvoiceNumber == null) { return true; }
if ((object)left.InvoiceNumber == null || (object)right.InvoiceNumber == null) { return false; }
return left.InvoiceNumber == right.InvoiceNumber;
}
public int GetHashCode(InvoiceItem item)
{
return item.InvoiceNumber == null ? 0 : item.InvoiceNumber.GetHashCode();
}
}
public class InvoiceItems : List<InvoiceItem>{ }
我的目标是使用几千个InvoiceItems
对象填充aBunchOfInvoiceItems
对象(我们将其称为InvoiceItem
),然后执行:
InvoiceItems distinctItems = aBunchOfInvoiceItems.Distinct();
当我设置此代码并运行它时,我收到一条错误
无法将类型'System.Collections.Generic.IEnumerable'隐式转换为'InvoiceReader.Form1.InvoiceItems'。存在显式转换(您是否错过了演员?)
我不明白如何解决这个问题。我应该采取不同的方法吗?非常感谢任何建议。
答案 0 :(得分:5)
Distinct
返回通用IEnumerable<T>
。它不返回InvoiceItems
实例。事实上,在窗帘后面它返回一个代理对象,该对象实现一个只在需要时访问的迭代器(即当你迭代它时)。
您可以通过调用List<>
将其明确强制转换为.ToList()
。但是仍然需要将其转换为自定义列表类型。最简单的方法可能是拥有一个合适的构造函数,并调用:
public class InvoiceItems : List<InvoiceItem> {
public InvoiceItems() { }
// Copy constructor
public InvoiceItems(IEnumerable<InvoiceItems> other) : base(other) { }
}
// …
InvoiceItems distinctItems = new InvoiceItems(aBunchOfInvoiceItems.Distinct());
答案 1 :(得分:4)
如果未向Distinct
提供比较器,则会使用EqualityComparer<T>.Default
。这将尝试使用IEquatable<T>
接口,如果缺少此接口,则返回Equals(object other)
上声明的普通旧object
方法。对于散列,它将使用GetHashCode()
方法,也在object
上声明。由于界面尚未按您的类型实现,并且上述方法都没有被覆盖,因此存在一个很大的问题: Distinct
将仅依赖于引用相等,这是不是你想要的。
当想要编写与类型本身分离的相等比较器时,通常使用IEqualityComparer<T>
接口。另一方面,当一个类型想要能够将自己的实例与另一个实例进行比较时;它通常实现IEquatable<T>
。我建议其中一个:
InvoiceItem
来实施IEquatable<InvoiceItem>
。InvoiceItemComparer : IEqualityComparer<InvoiceItem>
类型,然后调用invoiceItems.Distinct(new InvoiceItemComparer());
invoiceItems.Distinct(new InvoiceItem());
答案 2 :(得分:3)
很简单,aBunchOfInvoiceItems.Distinct()
会返回IEnumerable<InvoiceItem>
,而您正试图将其分配给非IEnumerable<InvoiceItem>
的内容。
但是,InvoiceItems
的基类有一个构造函数,它接受这样的对象,所以你可以使用它:
public class InvoiceItems : List<InvoiceItem>
{
public InvoiceItems(IEnumerable<InvoiceItem> items)
base(items){}
}
然后你可以使用:
InvoiceItems distinctItems = new InvoiceItems(aBunchOfInvoiceItems.Distinct());
尽管如此,我认为从List<InvoiceItem>
推导出来并没有多大好处,所以我可能更倾向于:
List<InvoiceItem> distinctItems = aBunchOfInvoiceItems.Distinct().ToList();
答案 3 :(得分:1)
该错误与您的班级InvoiceItems
有关,该班级继承自List<InvoiceItem>
。
Distinct
返回IEnumerable<InvoiceItem>
:InvoiceItems
是一种非常具体的IEnumerable<InvoiceItem>
类型,但任何IEnumerable<InvoiceItem>
都不一定是InvoiceItems
。
一个解决方案可能是使用隐式转换运算符,如果这是你想要做的事情: Doh,完全忘记你不能转换为/从接口转换(感谢Saed)
public class InvoiceItems : List<InvoiceItem>
{
public InvoiceItems(IEnumerable<InvoiceItem> items) : base(items) { }
}
其他注意事项:
List<T>
通常很糟糕。而是实施IList<T>
。答案 4 :(得分:0)
除了其他答案处理的自定义类vs IEnumerable问题之外,您的代码还有一个主要问题。您的类实现IEqualityComparer
而不是IEquatable
。使用Distinct
时,要过滤的项必须自己实现IEquatable,或者必须使用带有IEqualityComparer参数的重载。按照现在的情况,您对Distinct的调用不会根据您提供的IEqualityComparer Equals和GetHashCode方法过滤项目。
IEqualityComparer应由另一个类实现,而不是被比较的类。如果一个类知道如何比较自己,比如你的InvoiceItem
类,它应该实现IEquatable。