我曾经认为List<T> is considered dangerous。我的观点是,我认为默认(T)不是一个安全的返回值! Many other people think so too请考虑以下事项:
List<int> evens = new List<int> { 0, 2, 4, 6, , 8};
var evenGreaterThan10 = evens.Find(c=> c > 10);
// evenGreaterThan10 = 0 #WTF
值类型的默认值(T)为0,因此返回0是goona以上代码段!
我不喜欢这个,所以我添加了一个名为TryFind的扩展方法,它返回一个布尔值并接受除Predicate之外的输出参数,类似于着名的TryParse方法。
编辑:
这是我的TryFind扩展方法:
public static bool TryFind<T>(this List<T> list, Predicate<T> predicate, out T output)
{
int index = list.FindIndex(predicate);
if (index != -1)
{
output = list[index];
return true;
}
output = default(T);
return false;
}
您在通用列表中查找的方法是什么?
答案 0 :(得分:15)
我没有。我做.Where()
evens.Where(n => n > 10); // returns empty collection
evens.Where(n => n > 10).First(); // throws exception
evens.Where(n => n > 10).FirstOrDefault(); // returns 0
第一种情况只是返回一个集合,所以我可以简单地检查计数是否大于0来知道是否有任何匹配。
当使用第二个时,我在一个try / catch块中包装,该块以规范方式处理InvalidOperationException以处理空集合的情况,并重新抛出(冒泡)所有其他异常。这是我使用最少的一个,因为我不喜欢编写try / catch语句,如果我可以避免它。
在第三种情况下,当没有匹配时代码返回默认值(0)我没关系 - 毕竟,我明确地说“让我得到第一个或默认”值。因此,我可以阅读代码并理解为什么如果我遇到问题就会发生。
<强>更新强>
对于.NET 2.0用户,我会不推荐评论中建议的hack。它违反了.NET的许可协议,任何人都不会支持它。
相反,我认为有两种方法:
升级。在2.0上运行的大部分内容将在3.5上运行(或多或少)。而且3.5(而不仅仅是LINQ)中有很多真的值得升级以使其可用。由于4.0的新CLR运行时版本,2.0和4.0之间的更改比2.0和3.5之间有更多,但如果可能,我建议一直升级到4.0。实际上没有充分的理由坐在一个框架版本中编写新代码,该框架有3个主要版本(是的,我认为3.0,3.5和4.0都是主要的...),因为你正在使用它。 / p>
找到解决Find
问题的方法。我建议您只使用FindIndex
,因为在找不到任何内容时返回的-1永远不会模糊,或者像您一样使用FindIndex
实现某些内容。我不喜欢out
语法,但在我编写一个不使用它的实现之前,我需要一些关于你什么时候找不到的东西的输入。
在此之前,TryFind
可以被认为是正常的,因为它与.NET中的先前功能一致,例如Integer.TryParse
。并且你确实得到了一个很好的方法来处理没有找到的事情
List<Something> stuff = GetListOfStuff();
Something thing;
if (stuff.TryFind(t => t.IsCool, thing)) {
// do stuff that's good. thing is the stuff you're looking for.
}
else
{
// let the user know that the world sucks.
}
答案 1 :(得分:5)
您可以使用Find
代替FindIndex
。它返回找到的元素的索引,如果没有找到元素,则返回-1
。
http://msdn.microsoft.com/en-us/library/x1xzf2ca%28v=VS.80%29.aspx
答案 2 :(得分:2)
如果您知道列表中没有default(T)
值,或者default(T)
返回值不是结果,那就没关系。
您可以轻松实施自己的。
public static T Find<T>(this List<T> list, Predicate<T> match, out bool found)
{
found = false;
for (int i = 0; i < list.Count; i++)
{
if (match(list[i]))
{
found = true;
return list[i];
}
}
return default(T);
}
并在代码中:
bool found;
a.Find(x => x > 5, out found);
其他选择:
evens.First(predicate);//throws exception
evens.FindAll(predicate);//returns a list of results => use .Count.
这取决于您可以使用的框架版本。
答案 3 :(得分:2)
我在Reddit上给出了同样的答案。
Enumerable.FirstOrDefault( predicate )
答案 4 :(得分:1)
首先拨打Exists()
电话有助于解决问题:
int? zz = null;
if (evens.Exists(c => c > 10))
zz = evens.Find(c => c > 10);
if (zz.HasValue)
{
// .... etc ....
}
稍微啰嗦,但做得好。
答案 5 :(得分:1)
如果T的默认值出现问题,请使用Nullable获取更有意义的默认值:
List<int?> evens = new List<int?> { 0, 2, 4, 6, 8 };
var greaterThan10 = evens.Find(c => c > 10);
if (greaterThan10 != null)
{
// ...
}
这也不需要首先调用Exists()。
答案 6 :(得分:0)
只是为了好玩
evens.Where(c => c > 10)
.Select(c => (int?)c)
.DefaultIfEmpty(null)
.First();
答案 7 :(得分:0)
受上述Jaroslav Jandek的启发,我将进行扩展,如果匹配则返回布尔值true,如果匹配则将对象传递出去:
static class Extension
{
public static bool TryFind<T>(this List<T> list, Predicate<T> match, out T founditem)
{
for (int i = 0; i < list.Count; i++)
{
if (match(list[i]))
{
founditem = list[i];
return true;
}
}
founditem = default(T);
return false;
}
}
然后可以在List对象'a'上使用它,并使用'out var'语法像这样调用:
if (a.TryFind(x => x > 5, out var founditem)){
//enter code here to use 'founditem'
};