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

2 つのリストを比較して、他のリストにない項目のみを含む新しいリストを返したい場合は、次のようにします。

var newlist = list1.Except(list2);

これをカスタムクラスに拡張するにはどうすればよいですか? 私が持っているとしましょう:

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

これを返してほしい:

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

この疑似コードは明らかに機能しませんが、うまくいけば、私がやろうとしていることを示しています。この shoudl は、次のアイテムを含むカスタム クラスのワインセラーのリストを返します。

2012 キャンティ 12

2011 キャンティ 6

ありがとう。

4

5 に答える 5

17

ブラックリストとして使用するオブジェクトExceptのコレクションがないため、ここでは使用したくありません。WineCellarあなたが持っているのは、「これこれのワイン名のオブジェクトは欲しくない」というルールの集まりです。

したがって、次のように単純に使用することをお勧めしますWhere

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

人間が読める形式:

ワイン名が例外のリストにないすべての WineCellar が必要です。

余談ですが、WineCellarクラス名は少し誤解を招きます。これらのオブジェクトは地下室ではなく、インベントリ アイテムです。

于 2013-07-22T08:20:24.907 に答える
7

1 つの解決策は、拡張メソッドを使用することです。

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

exceptionWineListは aですstring[]が、originalWineCellarList<WineCellar>WineCellarはありません。したがって、これらの間stringで を実行しても意味がありません。Except

同じように簡単にできますが、

// 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);
    }
}

これにより、WineCellars をstrings に等しくすることができます。


ただし、モデルを作り直すとしたら、次のようになります。

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> 
{
    ...
}

次に、比較する方法がいくつかあることがわかります。 のプロパティの1 つまたは複数でWineフィルターをかけたいと思うかもしれません。したがって、私は自分自身にある程度の柔軟性を与えたくなるかもしれませんが、CellarWine

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 に答える
0

このような拡張メソッドをジェネリック クラスで直接使用するには、コンパレータを実装する必要があります。これは、Equal と GetHashCode の 2 つのメソッドで構成されます。それらを WineCellar クラスに実装する必要があります。 2 番目の例に注意してください

ハッシュベースのメソッドは、基本的な「List.Contains...」実装よりもはるかに高速であることに注意してください。

于 2013-07-22T10:37:10.927 に答える