选择List<Car>
。每个Car
都有一个标识它的唯一索引,比如RegNumber
,然后是另一个描述它的属性 - 比如说Color
。
我想
这就是我目前正在这样做的方式,我在问是否有更好,更有效的方式来做这件事?
Car car = CarsForSale.Find(c => c.RegNumber == 5);
if (car != null)
{
foreach (Car car in CarsForSale)
{
if (car.RegNumber == 5)
{
car.Color = "Red";
break;
}
}
}
else
{
CarsForSale.Add(new Car(5, "Red"));
}
Save(CarsForSale);
修改 没有多辆汽车具有相同的reg - RegNumber是问题中所述的唯一。
这真的只是一个愚蠢的@ ss时刻,无论如何代码审查都会发现。感谢所有回复,并且没有嘲笑我明显的stoopid问题。当然,从集合返回的item /元素是一个引用,所以绝对没有必要再次遍历列表...我想我的头撞墙。
答案 0 :(得分:4)
首先,您不需要在循环中对car.RegNumber == 5
进行测试 - 您已经从声明中找到了第一辆符合此标准的汽车:
Car car = CarsForSale.Find(c => c.RegNumber == 5);
事实上,你的整个循环是多余的,你可以拥有:
if (car != null)
{
car.Color = "Red";
}
else
{
CarsForSale.Add(new Car(5, "Red"));
}
除非您想要找到RegNumber
等于5的所有车辆,否则您的第一行不正确,因为它只会找到符合条件的第一辆车。要找到你想要的所有汽车:
var cars = CarsForSale.Where(c => c.RegNumber == 5);
foreach (Car car in cars)
{
car.Color = "Red";
}
if (!car.Any())
{
CarsForSale.Add(new Car(5, "Red"));
}
使用原始代码,编译器应警告您,循环中car
的重新定义会隐藏原始定义(我引用的那个)。
答案 1 :(得分:2)
为什么在已经有结果的情况下重新遍历列表?
这将取得同样的结果:
Car car = CarsForSale.Find(c => c.RegNumber == 5);
if (car != null)
{
car.Color = "Red";
}
else
{
CarsForSale.Add(new Car(5, "Red"));
}
Save(CarsForSale);
Find
CarsForSale
方法的结果(如果返回结果)将是引用类型,这意味着对car
的任何更改都将更改{{1}中的项目也是。我猜你认为CarsForSale
的结果会与Find
中的实际项目断开连接,因此不必要的foreach循环?
答案 2 :(得分:2)
一旦你从LINQ声明中找到了你正在寻找的汽车,就没有必要回过头去查找匹配项了:
Car car = CarsForSale.Where(c => c.RegNumber == 5).FirstOrDefault();
if(car != null)
{
car.Color = "Red";
}
else
{
CarsForSale.Add(new Car(5, "Red"));
}
Save(CarsForSale);
或者如果有多辆具有相同RegNumber的汽车:
var cars = CarsForSale.Where(c => c.RegNumber == 5);
if(cars.Any())
{
foreach(Car car in cars)
car.Color = "Red";
}
else
{
CarsForSale.Add(new Car(5, "Red"));
}
Save(CarsForSale);
答案 3 :(得分:2)
在回应此评论时,您还有其他几个答案:
如果有几辆车怎么办? RegNumber为5?
如果多辆汽车可能具有相同的RegNumber
,则调用Find
不是正确的方法。 Find
只是枚举列表以查找匹配项;你最好不要跳过它并保持foreach
循环。
但是,您可以使用Where
来简化您的代码:
var matches = CarsForSale.Where(c => c.RegNumber == 5);
int numMatches = 0;
foreach (Car match in matches )
{
match.Color = "Red";
++numMatches;
}
if (numMatches == 0)
{
CarsForSale.Add(new Car(5, "Red"));
}
整个foreach
循环是多余的:您基本上通过调用Find
完成了相同的工作。
所以代码可以简化:
Car car = CarsForSale.Find(c => c.RegNumber == 5);
if (car != null)
{
car.Color = "Red";
}
else
{
CarsForSale.Add(new Car(5, "Red"));
}
也就是说,如果您在List<Car>
RegNumber
之前查找汽车,那么使用Dictionary<int, Car>
代替List<Car>
是有意义的:
Car car;
if (CarsForSale.TryGetValue(5, out car))
{
car.Color = "Red";
}
else
{
CarsForSale[5] = car = new Car(5, "Red");
}
答案 4 :(得分:1)
正如Dan已经提到的,如果你有一个独特的属性,你应该将它作为Dictionary<TKey, TValue>
中的一个键。
因为检查某些内容是否在一个字典中是一个O(1)操作,而在一个列表中它是最坏情况下的O(n)(现在想象你的列表中有100万辆汽车)。
var carsForSale = new Dictionary<int, Car>();
//Create a car which you like to check
var checkCar = new Car(4, Color.Red);
//Use this approach if you want to change only a few properties
//of an existing item
if (carsForSale.ContainsKey(checkCar.RegNum))
{
carsForSale[checkCar.RegNum].Color = checkCar.Color;
}
else
{
carsForSale[4] = checkCar;
}
//If you have to take over ALL property settings, you can also
//forget the old item and take the new one.
//The index operator is smart enough to just add a new one
//or to delete an old and add the new in one step.
carsForSale[checkCar.RegNum] = checkCar;
汽车类的虚拟实现:
public class Car
{
public int RegNum { get; private set; }
public Color Color { get; set; }
public Car(int regNum)
: this(regNum, Color.Empty)
{ }
public Car(int regNum, Color color)
{
RegNum = regNum;
Color = color;
}
}
问题您使用词典的原因是,因为您想要明确告诉键是什么(汽车的RegNum属性),但您也可以使用Hashset<T>
如果您的Car对象能够正确实现Equals()
和GetHashCode()
,但这比您想象的要复杂一些。可以在Essentials C#一书中找到一个很好的解释。