Linq 将两个 ObservableCollection<T> 与 Except 进行比较

Linq compare two ObservableCollection<T> with Except

我已了解 IEqualityComparer 界面。这是我的代码(超过一千字)

static void Main(string[] args)
{
    var Send = new ObservableCollection<ProdRow>() {
        new ProdRow() { Code = "8718607000065", Quantity = 1 },
        new ProdRow() { Code = "8718607000911", Quantity = 10 }
    };
    var WouldSend = new ObservableCollection<ProdRow>() {
        new ProdRow() { Code = "8718607000065", Quantity = 1 },
        new ProdRow() { Code = "8718607000072", Quantity = 1 },
        new ProdRow() { Code = "8718607000256", Quantity = 1 },
        new ProdRow() { Code = "8718607000485", Quantity = 1 },
        new ProdRow() { Code = "8718607000737", Quantity = 1 },
        new ProdRow() { Code = "8718607000911", Quantity = 20 }
    };

    //var sendToMuch = Send.Except(WouldSend).ToList();
    //var sendToLittle = WouldSend.Except(Send).ToList();

    //if (sendToMuch.Any() || sendToLittle.Any())
    //    var notGood = true;
    //else
    //    var okay = true;

    var sendToMuch = Send.ToList();
    var sendToLittle = WouldSend.ToList();

    foreach (var s in Send) {
        var w = WouldSend.FirstOrDefault(d => d.Code.Equals(s.Code));

        if (w != null) {
            if (w.Quantity == s.Quantity) {
                sendToMuch.Remove(s);
                sendToLittle.Remove(w);
                continue;
            }
            if (w.Quantity > s.Quantity) {
                sendToLittle.Single(l => l.Code == w.Code).Quantity = (w.Quantity - s.Quantity);
                sendToMuch.Remove(s);
            } else {
                sendToMuch.Single(l => l.Code == w.Code).Quantity = (s.Quantity - w.Quantity);
                sendToLittle.Remove(s);
            }
        } else {
            sendToMuch.Add(s);
        }
    }
}

注释行我希望它能起作用...下面的内容是我最终得到的内容。

作为参考,这是我的 ProdRow class:

class ProdRow : INotifyPropertyChanged, IEqualityComparer<ProdRow>
{
    private string _code;
    private int _quantity;
    public string Code {
        get { return _code; }
        set {
            _code = value;
            OnPropertyChanged("Code");
        }
    }
    public int Quantity {
        get { return _quantity; }
        set {
            _quantity = value;
            OnPropertyChanged("Quantity");
        }
    }

    private void OnPropertyChanged(string v) {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(v));
    }

    public new bool Equals(object x, object y) {
        if (((ProdRow)x).Code.Equals(((ProdRow)y).Code) && ((ProdRow)x).Quantity == ((ProdRow)y).Quantity)
            return true;
        else
            return false;
    }
    public int GetHashCode(object obj) {
        return obj.GetHashCode();
    }
    public bool Equals(ProdRow x, ProdRow y) {
        if (x.Code.Equals(y.Code) && x.Quantity == y.Quantity)
            return true;
        else
            return false;
    }
    public int GetHashCode(ProdRow obj) {
        throw new NotImplementedException();
    }
    public event PropertyChangedEventHandler PropertyChanged;
}

我没想到评论部分会起作用,因为它不知道要减少数量等的整数,但我想知道是否有比我使用的解决方案更有效的方法来做到这一点(下面注释行)。也许把 collection 压扁成 string[]?

P.S。对不起 SendWouldSend

的 "PascalCase"

IEqualityComparer<T> 不是为 class 实施的正确接口,您希望比较其 实例 IEqualityComparer<T> 实现用于创建从被比较对象的 外部 进行比较的对象,当您需要重新定义两个对象的含义时,这一点变得很重要equal 无需访问这些对象的代码,或者当您需要根据上下文使用不同的语义来实现相等时。

强类型相等比较的正确接口是 IEquatable<T>。但是,在您的情况下,您只需要覆盖 ObjectEquals(object)GetHashCode():

public new bool Equals(object obj) {
    if (obj == this) return true;
    var other = obj as ProdRow;
    if (other == null) return false;
    return Code.Equals(other.Code) && Quantity == other.Quantity;
}
public int GetHashCode() {
    return 31*Code.GetHashCode() + Quantity;
}

就计算量而言,你可以用负数来计算,GroupBy:

var quantityByCode = WouldSend.Select(p => new {p.Code, p.Quantity})
    .Concat(Send.Select(p => new {p.Code, Quantity = -p.Quantity}))
    .GroupBy(p => p.Code)
    .ToDictionary(g => g.Key, g => g.Sum(p => p.Quantity));
var tooLittle = quantityByCode
    .Where(p => p.Value > 0)
    .Select(p => new ProdRow {Code = p.Key, Quantity = p.Value})
    .ToList();
var tooMuch = quantityByCode
    .Where(p => p.Value < 0)
    .Select(p => new ProdRow {Code = p.Key, Quantity = -p.Value})
    .ToList();