List.Find <t>被认为是危险的吗?什么是更好的方法做List <t> .Find(Predicate <t>)?</t> </t> </t>

时间:2010-07-08 10:46:05

标签: c# .net

我曾经认为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;  
}  

您在通用列表中查找的方法是什么?

8 个答案:

答案 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的许可协议,任何人都不会支持它。

相反,我认为有两种方法:

  1. 升级。在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>

  2. 找到解决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'
};