我目前在提取所需汽车时遇到问题,该汽车具有轮胎和发动机组合,属于列表 tires
和 engines
列表的一部分。
我有这个设置:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public class EngineType
{
public string engineName;
}
public class Tire
{
public string tireName;
}
public class Car
{
public EngineType enginetype;
public Tire tire;
}
public static void Main()
{
List<Car> cars = new List<Car>();
cars.Add(new Car{enginetype = new EngineType{engineName = "enginea"}, tire = new Tire{tireName = "tirea"}});
cars.Add(new Car{enginetype = new EngineType{engineName = "engineb"}, tire = new Tire{tireName = "tireb"}});
cars.Add(new Car{enginetype = new EngineType{engineName = "enginec"}, tire = new Tire{tireName = "tirec"}});
List<Tire> tires = new List<Tire>();
tires.Add(new Tire{tireName = "tirea"});
tires.Add(new Tire{tireName = "tired"});
tires.Add(new Tire{tireName = "tiree"});
List<EngineType> engineTypes = new List<EngineType>();
engineTypes.Add(new EngineType{engineName = "enginea"});
engineTypes.Add(new EngineType{engineName = "engined"});
engineTypes.Add(new EngineType{engineName = "enginec"});
var selectedcars = cars.Where(x=> engineTypes.Contains(x.enginetype) && tires.Contains(x.tire));
Console.WriteLine(selectedcars.Count);
}
}
这虽然给了我一个错误,因为 selectedcars
不包含汽车
为什么不呢?
答案 0 :(得分:3)
您比较具有相同值的不同对象,因此它无法工作。 您有多种选择来解决这个问题:
第一个选项:使用值
IEnumerable<Car> selectedcars = cars.Where(x => engineTypes.Select(e => e.engineName).Contains(x.enginetype.engineName) && tires.Select(t => t.tireName).Contains(x.tire.tireName));
这一行将提取值来比较它们,无论它是否是同一个对象。 显然,如果您根本不关心对象,您就可以根本不使用它们:
var tires = new []
{
"tirea",
"tired",
"tiree"
};
var engineTypes = new []
{
"enginea",
"engined",
"enginec"
};
IEnumerable<Car> selectedcars = cars.Where(x => engineTypes.Contains(x.enginetype.engineName) && tires.Contains(x.tire.tireName));
顺便说一下,如果你只需要一个字符串就足够了,而你又不想真正拥有“Tire”或“EngineType”类,那么你可以枚举。
第二个选项:记录
如果您使用的是 C# 9 或更高版本并且您只关心 Tyre 和 EngineType 内部的值(因此只关心内部字段的值,无论它们是否是不同的对象),您可以使用“记录”功能。使用该机制比较两个对象时,只会考虑值,而不考虑引用:
public record EngineType
{
public string engineName;
}
public record Tire
{
public string tireName;
}
第三个选项:实现 Equals(由@Tim Schmelter 在评论中提出)
如果您喜欢以前的解决方案,但不能使用“记录”功能,则可以确保您的类实现“IEquatable”接口。这样你就可以表明你想如何比较(而不是参考):
public class EngineType
{
public string engineName;
public bool Equals(EngineType other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return engineName == other.engineName;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((EngineType) obj);
}
public override int GetHashCode()
{
return (engineName != null ? engineName.GetHashCode() : 0);
}
}
public class Tire
{
public string tireName;
protected bool Equals(Tire other)
{
return tireName == other.tireName;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Tire) obj);
}
public override int GetHashCode()
{
return (tireName != null ? tireName.GetHashCode() : 0);
}
}
(此代码已生成所以我认为它是正确的)
您的查询仍然可以相同:
var selectedcars = cars.Where(x => engineTypes.Contains(x.enginetype) && tires.Contains(x.tire));
第四个选项:实现比较器(由@Tim Schmelter 在评论中提出)
如果您仍然喜欢前面的选项,但您不能修改对象本身(也许它已经生成或者是 nuget 包的一部分,...),您可以在它们之外实现一个比较器:
public class EngineTypeComparer : IEqualityComparer<EngineType>
{
public bool Equals(EngineType x, EngineType y)
{
return x.engineName == y.engineName;
}
public int GetHashCode(EngineType obj)
{
return obj.x.engineName.GetHashCode(); }
}
public class TireComparer : IEqualityComparer<Tire>
{
public bool Equals(Tire x, Tire y)
{
return x.tireName == y.tireName;
}
public int GetHashCode(Tire obj)
{
return obj.tireName.GetHashCode();
}
}
(空检查在那些比较器中没有实现,但显然应该实现);
并且在查询中,您必须指定要使用的比较器:
var selectedcars = cars.Where(x => engineTypes.Contains(x.enginetype, new EngineTypeComparer()) && tires.Contains(x.tire, new TireComparer()));