C# 列表。自定义类上除外
假设我有一个自定义类:C# 列表。自定义类上除外,c#,linq,C#,Linq,假设我有一个自定义类: public class WineCellar { public string year; public string wine; public double nrbottles; } List<WineCellar> orignialwinecellar = List<WineCellar>(); 假设我现在有一个自定义类的列表: public class WineCellar { public strin
public class WineCellar
{
public string year;
public string wine;
public double nrbottles;
}
List<WineCellar> orignialwinecellar = List<WineCellar>();
假设我现在有一个自定义类的列表:
public class WineCellar
{
public string year;
public string wine;
public double nrbottles;
}
List<WineCellar> orignialwinecellar = List<WineCellar>();
我知道,如果我想比较两个列表并返回一个新列表,其中只包含不在另一个列表中的项目,我会:
var newlist = list1.Except(list2);
如何将其扩展到自定义类?假设我有:
string[] exceptionwinelist = {"Chardonay", "Riesling"};
我想把这个退回:
List<WineCellar> result = originalwinecellar.wine.Except(exceptionwinelist);
List result=originalwinecillar.wine.Except(例外WineList);
这个伪代码显然不起作用,但希望能说明我正在尝试做什么。然后返回一个自定义类winecellar列表,其中包含以下项目:
2012基安蒂12
2011基安蒂6号
谢谢。除了这里,你不想使用
,因为你没有酒窖对象的集合来用作黑名单。你所拥有的是一系列规则:“我不想要有这样或那样酒名的东西”
因此,最好只使用Where
:
List<WineCellar> result = originalwinecellar
.Where(w => !exceptionwinelist.Contains(w.wine))
.ToList();
列表结果=原始酒窖
.Where(w=>!exceptionwinelist.Contains(w.wine))
.ToList();
以人类可读的形式:
我希望所有酒窖的葡萄酒名称不在例外列表中
另外,winecillar
类名有点误导;这些对象不是酒窖,而是库存物品。一种解决方案是使用扩展方法:
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;
}
}
}
}
公共静态类扩展
{
公共静态IEnumerable除外(此列表酒窖,IEnumerable葡萄酒)
{
foreach(酒窖中的var酒窖)
{
如果(!葡萄酒。包含(酒窖。葡萄酒))
{
产量回收酒窖;
}
}
}
}
然后像这样使用它:
List<WineCellar> result = originalwinecellar.Except(exceptionwinelist).ToList();
List result=originalwinecillar.Except(exceptionwinelist.ToList();
例外WineList
是一个字符串[]
,但是原始酒窖
是一个列表
,Winecella
不是字符串
,因此在它们之间执行之外的是没有意义的
你可以很容易地做到
// use HashSet for look up performance.
var exceptionWineSet = new HashSet<string>(exceptionWineList);
var result = orginalWineCellar.Where(w => !exceptionWineSet.Contains(w.Wine));
然后,您可以看到有几种方法可以比较葡萄酒
,我可能希望对酒窖
的一个或多个葡萄酒
属性进行过滤。因此,我可能会被要求给自己一些灵活性
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;
}
}
要直接将这种扩展方法用于泛型类,您应该实现comparator。它由两个方法组成:Equal和GetHashCode。您应该在WineCellar类中实现它们。
.
请注意,基于哈希的方法比基本的“List.Contains…”实现快得多 我也有同样的问题要解决。我尝试了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; }
}
}
}
静态类助手
{
公共静态IEnumerable除外(此列表x,列表y)
{
(X中的var席)
{
bool-found=false;
foreach(y中的变量yi){if(xi.Name==yi.Name){found=true;}}
如果(找到){收益返回席;}
}
}
}
这对我有用。如果需要,您可以在if子句中添加多个字段。如果您非常频繁地进行此排序,或者异常列表包含许多项,您可能希望将异常列表存储为哈希集
,以使包含
功能更快。然而,像往常一样,将这两种方法作为HashSet
进行基准测试确实会产生不可忽略的成本。@Jon,不错的一个。。。我想Waayy在这里太远了。。。但是,请注意演员表中的列表,-)@是的,我错过了——现在修好了。@ScottChamberlain:没错。我没有走那么远,因为如果这真的需要大量处理,那么对于输入数据来说,直接的列表也不是一个好的选择,因此结果与原始问题没有太多共同之处。每次我在数组上看到一个使用Contains
的问题。我只是想帮助别人从我的错误中吸取教训:)很好的扩展方法!我要做的唯一不同的事情是将第一个参数更改为this IEnumerable Cell
,使用contains to List会导致非常糟糕的性能,因为contains on List是O(n)操作,它在每个步骤上都会执行。在这种情况下,应该通过HashSet或Dictionary完成。@gleb.kudr我知道。除此之外,这是一个不同的问题(如何优化运行缓慢的代码),取决于输入。当然继续并将wines
参数隐藏在HashSet
中。最好的方法是实现GetHashCode。这种“此列表”扩展将泛型的核心概念搞砸了,因为您有一个通用的实现。
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));
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; }
}
}
}