2

我已经按照这篇文章的建议尝试让 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对象(我们称之为) ,然后执行:aBunchOfInvoiceItemsInvoiceItem

InvoiceItems distinctItems = aBunchOfInvoiceItems.Distinct();  

当我设置并运行此代码时,我收到一条错误消息

无法将类型“System.Collections.Generic.IEnumerable”隐式转换为“InvoiceReader.Form1.InvoiceItems”。存在显式转换(您是否缺少演员表?)

我不明白如何解决这个问题。我应该采取不同的方法吗?非常感谢任何建议。

4

5 回答 5

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());
于 2010-11-19T15:42:33.340 回答
4

康拉德鲁道夫的回答应该解决您的编译问题。这里还有一个重要的语义正确性问题被遗漏了:实际上没有一个相等逻辑会被使用。

当没有提供比较器时Distinct,它使用EqualityComparer<T>.Default. 这将尝试使用该IEquatable<T>接口,如果缺少该接口,则回退到在上Equals(object other)声明的普通旧方法object。对于散列,它将使用GetHashCode()方法,也声明在object. 由于该接口尚未由您的类型实现,并且上述方法均未被覆盖,因此存在一个大问题:Distinct只会退回到 reference-equality,这不是您想要的。

IEqualityComparer<T>当人们想要编写一个与类型本身分离的等式比较器时,通常会使用该接口。另一方面,当一个类型希望能够将自己的一个实例与另一个实例进行比较时;它通常实现IEquatable<T>. 我建议其中之一:

  1. 改为InvoiceItem实施IEquatable<InvoiceItem>
  2. 将比较逻辑移动到单独的InvoiceItemComparer : IEqualityComparer<InvoiceItem>类型,然后调用 invoiceItems.Distinct(new InvoiceItemComparer());
  3. 如果您想快速破解现有代码,您可以这样做invoiceItems.Distinct(new InvoiceItem());
于 2010-11-19T16:10:56.487 回答
3

很简单,aBunchOfInvoiceItems.Distinct()返回 anIEnumerable<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();
于 2010-11-19T15:44:47.037 回答
1

该错误与您的类有关InvoiceItems,该类继承自List<InvoiceItem>.

Distinct返回一个IEnumerable<InvoiceItem>:InvoiceItems是一个非常特殊的类型IEnumerable<InvoiceItem>,但 anyIEnumerable<InvoiceItem>不一定是一个InvoiceItems.

一种解决方案可能是使用隐式转换运算符,如果这是您想要做的: Doh,完全忘记了您不能转换为接口/从接口转换(感谢 Saed)

public class InvoiceItems : List<InvoiceItem>
{
    public InvoiceItems(IEnumerable<InvoiceItem> items) : base(items) { }
}

其他需要注意的事项:

  • 继承自List<T>通常是不好的。IList<T>改为实施。
  • 使用列表抛弃了 LINQ 的一大好处,即惰性求值。确保预取结果实际上是您想要做的。
于 2010-11-19T15:42:50.727 回答
0

除了其他答案处理的自定义类与 IEnumerable 问题之外,您的代码还存在一个主要问题。您的类实现IEqualityComparer而不是IEquatable. 当您使用Distinct时,被过滤的项目必须自己实现 IEquatable,或者您必须使用采用 IEqualityComparer 参数的重载。就目前而言,您对 Distinct 的调用不会根据您提供的 IEqualityComparer Equals 和 GetHashCode 方法过滤项目。

IEqualityComparer 应该由另一个类来实现,而不是被比较的类。如果一个类知道如何比较自己,就像你的InvoiceItem类一样,它应该实现 IEquatable。

于 2010-11-19T16:11:28.090 回答