簡體   English   中英

是 List.Find<T> 被認為是危險的? 有什么更好的方式來做 List<T> .Find(謂詞<T> )?

[英]Is List.Find<T> considered dangerous? What's a better way to do List<T>.Find(Predicate<T>)?

我曾經認為List<T> 被認為是危險的 我的觀點是,我認為 default(T) 不是一個安全的返回值! 許多其他人也這么認為考慮以下幾點:

List<int> evens = new List<int> { 0, 2, 4, 6, , 8};
var evenGreaterThan10 = evens.Find(c=> c > 10);
// evenGreaterThan10 = 0 #WTF

值類型的 default(T) 為 0,因此返回 0 是上面的代碼段!
我不喜歡這樣,所以我添加了一個名為 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;  
}  

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

我不。 我做.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 以了解是否有任何匹配項。

使用第二個時,我包裝在一個專門處理 InvalidOperationException 的 try/catch 塊中,以處理空集合的情況,並重新拋出(冒泡)所有其他異常。 這是我最少使用的一個,僅僅是因為如果我可以避免的話,我不喜歡編寫 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 都視為主要版本......),因為你正在使用那個版本。

  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. }

您可以使用FindIndex而不是Find 它返回找到的元素的索引,如果沒有找到元素,則返回-1

http://msdn.microsoft.com/en-us/library/x1xzf2ca%28v=VS.80%29.aspx

如果您知道列表中沒有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.

這取決於您可以使用的框架版本。

我在 Reddit 上給出的答案相同。

Enumerable.FirstOrDefault( predicate )

如果 T 的默認值有問題,可以使用 Nullable 來獲取更有意義的默認值:

List<int?> evens = new List<int?> { 0, 2, 4, 6, 8 };
var greaterThan10 = evens.Find(c => c > 10);
if (greaterThan10 != null) 
{
    // ...
}

這也不需要首先額外調用 Exists()。

首先調用Exists()將有助於解決問題:

int? zz = null;
if (evens.Exists(c => c > 10))
    zz = evens.Find(c => c > 10);

if (zz.HasValue)
{
    // .... etc ....
}

稍微啰嗦一點,但可以完成工作。

只是為了好玩

evens.Where(c => c > 10)
    .Select(c => (int?)c)
    .DefaultIfEmpty(null)
    .First();

受上面的Jaroslav Jandek 的啟發,我將創建一個擴展,如果匹配則返回 bool 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'
};

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM