7

假设我有一个自定义类:

public class WineCellar
{

    public string year;
    public string wine;
    public double nrbottles;
}

假设我现在有这个自定义类的列表:

List<WineCellar> orignialwinecellar = List<WineCellar>();

包含这些项目:

2012 Chianti 12

2011 Chianti 6

2012 Chardonay 12

2011 Chardonay 6

我知道,如果我想比较两个列表并返回一个新列表,其中只有不在另一个列表中的项目,我会这样做:

var newlist = list1.Except(list2);

如何将其扩展到自定义类?可以说我有:

string[] exceptionwinelist = {"Chardonay", "Riesling"};

我希望这个被退回:

List<WineCellar> result = originalwinecellar.wine.Except(exceptionwinelist);

这个伪代码显然不起作用,但希望能说明我正在尝试做的事情。然后,这应该返回一个包含以下项目的自定义类 winecellar 的列表:

2012 基安蒂 12

2011 基安蒂 6

谢谢。

4

5 回答 5

17

您真的不想在Except这里使用,因为您没有WineCellar可用作黑名单的对象集合。您所拥有的是一系列规则:“我不想要具有某某酒名的物品”。

因此最好简单地使用Where

List<WineCellar> result = originalwinecellar
    .Where(w => !exceptionwinelist.Contains(w.wine))
    .ToList();

以人类可读的形式:

我想要所有葡萄酒名称不在例外列表中的 WineCellars。

顺便说一句,WineCellar类名有点误导。这些物品不是地窖,它们是库存物品。

于 2013-07-22T08:20:24.907 回答
7

一种解决方案是使用扩展方法:

public static class WineCellarExtensions
{
    public static IEnumerable<WineCellar> Except(this List<WineCellar> cellar, IEnumerable<string> wines)
    {
        foreach (var wineCellar in cellar)
        {
            if (!wines.Contains(wineCellar.wine))
            {
                yield return wineCellar;
            }
        }
    }
}

然后像这样使用它:

List<WineCellar> result = originalwinecellar.Except(exceptionwinelist).ToList();
于 2013-07-22T08:21:49.740 回答
2

exceptionWineListis a string[]but originalWineCellaris a List<WineCellar>, WineCellaris not a string,因此Except在这些之间执行 an 是没有意义的。

你可以很容易地做到,

// use HashSet for look up performance.
var exceptionWineSet = new HashSet<string>(exceptionWineList);
var result = orginalWineCellar.Where(w => !exceptionWineSet.Contains(w.Wine));

我认为你在你的问题中暗示的是

WineCellar : IEquatable<string>
{
    ...
    public bool Equals(string other)
    {
        return other.Equals(this.wine, StringComparison.Ordinal);
    }
}

这允许您将 s 等同WineCellarstrings。


但是,如果我要重新设计你的模型,我会想出类似的东西,

enum WineColour
{
    Red,
    White,
    Rose
}

enum WineRegion
{
    Bordeaux,
    Rioja,
    Alsace,
    ...
}

enum GrapeVariety
{
    Cabernet Sauvignon,
    Merlot,
    Ugni Blanc,
    Carmenere,
    ...
}

class Wine
{
    public string Name { get; set; }
    public string Vineyard { get; set; }
    public WineColour Colour { get; set; }
    public WineRegion Region { get; set; }
    public GrapeVariety Variety { get; set; }
}

class WineBottle
{
    public Wine Contents { get; set; }
    public int Millilitres { get; set; }
    public int? vintage { get; set; }
}

class Bin : WineBottle
{
    int Number { get; set; }
    int Quantity { get; set; }
}

class Cellar : ICollection<WineBottle> 
{
    ...
}

然后,您可以看到有几种比较方法Wine,我可能想Cellar在一个或多个Wine属性上过滤 a 。因此,我可能会想给自己一些灵活性,

class WineComparer : EqualityComparer<Wine>
{
    [Flags]
    public Enum WineComparison
    {
        Name = 1,
        Vineyard= 2,
        Colour = 4,
        Region = 8,
        Variety = 16,
        All = 31
    }

    private readonly WineComparison comparison;

    public WineComparer()
        : this WineComparer(WineComparison.All)
    {
    }

    public WineComparer(WineComparison comparison)
    {
        this.comparison = comparison;
    }

    public override bool Equals(Wine x, Wine y)
    {
        if ((this.comparison & WineComparison.Name) != 0
            && !x.Name.Equals(y.Name))
        {
            return false;
        }

        if ((this.comparison & WineComparison.Vineyard) != 0
            && !x.Vineyard.Equals(y.Vineyard))
        {
            return false;
        }

        if ((this.comparison & WineComparison.Region) != 0
            && !x.Region.Equals(y.Region))
        {
            return false;
        }

        if ((this.comparison & WineComparison.Colour) != 0
            && !x.Colour.Equals(y.Colour))
        {
            return false;
        }

        if ((this.comparison & WineComparison.Variety) != 0
            && !x.Variety.Equals(y.Variety))
        {
            return false;
        }

        return true;
    }

    public override bool GetHashCode(Wine obj)
    {
        var code = 0;
        if ((this.comparison & WineComparison.Name) != 0)
        {
            code = obj.Name.GetHashCode();
        }

        if ((this.comparison & WineComparison.Vineyard) != 0)
        {
            code = (code * 17) + obj.Vineyard.GetHashCode();
        }

        if ((this.comparison & WineComparison.Region) != 0)
        {
            code = (code * 17) + obj.Region.GetHashCode();
        }

        if ((this.comparison & WineComparison.Colour) != 0)
        {
            code = (code * 17) + obj.Colour.GetHashCode();
        }

        if ((this.comparison & WineComparison.Variety) != 0)
        {
            code = (code * 17) + obj.Variety.GetHashCode();
        }

        return code;
    }
}

这可能看起来很费力,但它有一些用处。假设我们想要你地窖里除了红里奥哈以外的所有葡萄酒,你可以这样做,

var comparison = new WineComparer(
    WineComparison.Colour + WineComparison.Region);

var exception = new Wine { Colour = WineColour.Red, Region = WineRegion.Rioja }; 

var allButRedRioja = cellar.Where(c => 
    !comparison.Equals(c.Wine, exception));
于 2013-07-22T08:32:51.960 回答
2

我有这个完全相同的问题。我尝试了 Darren 的示例,但无法使其正常工作。

因此,我对 Darren 的示例进行了如下修改:

static class Helper
{
    public static IEnumerable<Product> Except(this List<Product> x, List<Product> y)
    {
        foreach(var xi in x)
        {
            bool found = false;
            foreach (var yi in y) { if(xi.Name == yi.Name) { found = true; } }
            if(!found) { yield return xi; }
        }
    }
}

这对我有用。如果需要,您可以在 if 子句中添加几个字段。

于 2017-12-19T19:46:41.417 回答
0

要直接将此类扩展方法与泛型类一起使用,您应该实现比较器。它由两个方法组成:Equal 和 GetHashCode。你应该在你的 WineCellar 类中实现它们。 注意第二个例子

请注意,基于散列的方法比基本的“List.Contains...”实现要快得多。

于 2013-07-22T10:37:10.927 回答