关于List<T>中的Exists 和Contains的区别
今天在做小项目的时候,有个需求,是比较List 中的对象是否存在过
而比较的对象是通过新建得来的(反序列化)
正当我使用Contains做比较的时候,
就发现问题了.
以下是测试代码
[TestMethod()]
public void RemoveDiscendantPowerTest() {
Power Parent = new Power
{
Alias = "testRoot",
CanDelete = false,
Description = "测试根目录",
id = 100,
PowerName = "测试根目录",
Childs = new List<Power>
{
new Power{
Alias = "testBranch1",
CanDelete = true,
Description = "枝干节点1",
id = 100100,
PowerName = "枝干节点1",
Childs = new List<Power>{
new Power{
Alias = "leaf1",
CanDelete = true,
Description = "叶子节点1",
id = 100100100,
PowerName = "叶子节点1"
},
new Power{
Alias = "leaf2",
CanDelete = true,
Description = "叶子节点2",
id = 100100101,
PowerName = "叶子节点2"
}
}
},
new Power{
Alias ="testBranch2",
CanDelete = true,
Description = "枝干节点2",
id = 100101,
PowerName = "枝干接点2",
Childs = new List<Power>{
new Power{
Alias = "leaf3",
CanDelete = true,
Description = "叶子节点3",
id = 100101100,
PowerName = "叶子节点3"
},
new Power{
Alias = "leaf4",
CanDelete = true,
Description = "叶子节点4",
id = 100101101,
PowerName = "叶子节点4"
}
}
}
}
}; // TODO: 初始化为适当的值
Power Discendant = new Power{
Alias = "leaf4",
CanDelete = true,
Description = "叶子节点4",
id = 100101101,
PowerName = "叶子节点4"
};
// TODO: 初始化为适当的值
Assert.IsTrue(Parent.Childs[1].Childs.Contains(Discendant), "测试失败,Contains 不能抓出");
}
测试结果如下:
结论是,新建的一模一样的对象却在Contains 中返回的false,未能通过测试
为什么会出现这种问题呢?
答案很简单:Contains比较的是存储的内存地址是否相同,而不是值相同.
我们更改下测试,就能发现问题了
/// <summary>
///RemoveDiscendantPower 的测试
///</summary>
[TestMethod()]
public void RemoveDiscendantPowerTest() {
Power Parent = new Power
{
Alias = "testRoot",
CanDelete = false,
Description = "测试根目录",
id = 100,
PowerName = "测试根目录",
Childs = new List<Power>
{
new Power{
Alias = "testBranch1",
CanDelete = true,
Description = "枝干节点1",
id = 100100,
PowerName = "枝干节点1",
Childs = new List<Power>{
new Power{
Alias = "leaf1",
CanDelete = true,
Description = "叶子节点1",
id = 100100100,
PowerName = "叶子节点1"
},
new Power{
Alias = "leaf2",
CanDelete = true,
Description = "叶子节点2",
id = 100100101,
PowerName = "叶子节点2"
}
}
},
new Power{
Alias ="testBranch2",
CanDelete = true,
Description = "枝干节点2",
id = 100101,
PowerName = "枝干接点2",
Childs = new List<Power>{
new Power{
Alias = "leaf3",
CanDelete = true,
Description = "叶子节点3",
id = 100101100,
PowerName = "叶子节点3"
},
new Power{
Alias = "leaf4",
CanDelete = true,
Description = "叶子节点4",
id = 100101101,
PowerName = "叶子节点4"
}
}
}
}
}; // TODO: 初始化为适当的值
Power Discendant = new Power{
Alias = "leaf4",
CanDelete = true,
Description = "叶子节点4",
id = 100101101,
PowerName = "叶子节点4"
};
// TODO: 初始化为适当的值
//Assert.IsTrue(Parent.Childs[1].Childs.Contains(Discendant), "测试失败,Contains不能抓出");
Power test = Parent.Childs[1].Childs[1];
Assert.IsTrue(Parent.Childs[1].Childs.Contains(test), "测试失败,甚至连内存引用都扯淡");
}
结果为
很显然,当我直接从Parent中取得一个对象的时候,Contains就会抓住这个对象
那么,如果说我需要值的比较的话,怎样做到呢?
很简单,我们可以定义一个predicate 委托来指定怎样进行比较:
/// <summary>
///RemoveDiscendantPower 的测试
///</summary>
[TestMethod()]
public void RemoveDiscendantPowerTest() {
Power Parent = new Power
{
Alias = "testRoot",
CanDelete = false,
Description = "测试根目录",
id = 100,
PowerName = "测试根目录",
Childs = new List<Power>
{
new Power{
Alias = "testBranch1",
CanDelete = true,
Description = "枝干节点1",
id = 100100,
PowerName = "枝干节点1",
Childs = new List<Power>{
new Power{
Alias = "leaf1",
CanDelete = true,
Description = "叶子节点1",
id = 100100100,
PowerName = "叶子节点1"
},
new Power{
Alias = "leaf2",
CanDelete = true,
Description = "叶子节点2",
id = 100100101,
PowerName = "叶子节点2"
}
}
},
new Power{
Alias ="testBranch2",
CanDelete = true,
Description = "枝干节点2",
id = 100101,
PowerName = "枝干接点2",
Childs = new List<Power>{
new Power{
Alias = "leaf3",
CanDelete = true,
Description = "叶子节点3",
id = 100101100,
PowerName = "叶子节点3"
},
new Power{
Alias = "leaf4",
CanDelete = true,
Description = "叶子节点4",
id = 100101101,
PowerName = "叶子节点4"
}
}
}
}
}; // TODO: 初始化为适当的值
Power Discendant = new Power{
Alias = "leaf4",
CanDelete = true,
Description = "叶子节点4",
id = 100101101,
PowerName = "叶子节点4"
};
// TODO: 初始化为适当的值
//Assert.IsTrue(Parent.Childs[1].Childs.Contains(Discendant), "测试失败,Contains不能抓出");
//Power test = Parent.Childs[1].Childs[1];
//Assert.IsTrue(Parent.Childs[1].Childs.Contains(test), "测试失败,甚至连内存引用都扯淡");
Assert.IsTrue(Parent.Childs[1].Childs.Exists(delegate(Power t) {
return t.id == 100101101;
}));
}
(此处使用匿名委托代替了显式声明一个Predicate 委托)
测试轻松通过
ok,委托的比较执行成功.
结论
================================
List<T> 中的Contains 是对对象的"内存检测",
如果想查看List<T> 中是否有个对象的值 跟你声明的对象的值相同
则我们需要Exists 方法,且实现一个Predicate 委托来指定比较的方式
================================