2014-03-13 65 views
2

說我有一個三元操作:擴展到隱藏三元運算符

db.Scanners.FirstOrDefault(s => s.ScannerID==24) != null 
     ? db.Scanners.FirstOrDefault(s => s.ScannerID==24).FriendlyName 
     : "N/A"; 

我想寫一個LINQ擴展隱藏操作。我最好的嘗試是:

public static object PropertyOrNull<T>(this T source, string property) 
{ 
    if (source == null || source.GetType().GetProperty(property) == null) 
     return null; 

    return source.GetType().GetProperty(property).GetValue(source, null); 
} 

,我可以通過現在稱之爲:

(string)db.Scanners.FirstOrDefault(s => s.ScannerID == 24) 
      .PropertyOrNull("FriendlyName"); 

但是,這是很醜陋的。有沒有辦法重新工作,我不必裝箱和取消退貨價值?我對擴展/泛型很新,所以如果這很簡單,請原諒我。

+2

這是不是LINQ擴展。這只是一個擴展。 LINQ只是'System.Linq'命名空間中擴展方法的集合。你不是在那裏添加一個,你要在別的地方添加你自己的擴展。 – Servy

回答

4

你可以做這樣的事情:

public static TProperty TryGetValue<T, TProperty>(
    this T source, 
    Func<T, TProperty> getter, 
    TProperty defaultValue = default(TProperty)) 
{ 
    if (source == null) 
     return defaultValue; 

    return getter(source); 
} 

使用它像:

db.Scanners.FirstOrDefault(s => s.ScannerID == 24) 
      .TryGetValue(s => s.FriendlyName, "N/A"); 

這種方法比使用反射更快,而且更安全,因爲在編譯時會檢測到無效的屬性名稱。它也是強類型的,這意味着你不需要輸出結果。

不需要一個新的擴展方法,另一種方法是調用FirstOrDefault之前項目的順序:

db.Scanners.Select(s => s.FriendlyName) 
      .FirstOrDefault() ?? "N/A"; 
+0

太棒了。特別是使用可選的defaultValue。非常感謝 – Jonesopolis

2

因此,首先,而不是反思,你應該簡單地接受委託。您還可以使用第二個通用的參數來處理返回實際對象的類型:

public static TResult Use<T, TResult>(this T obj, Func<T, TResult> selector) 
    where TResult : class 
    where T : class 
{ 
    if (obj == null) 
     return null; 
    return selector(obj); 
} 

然後,您可以調用它像這樣:

var str = something.Use(s => s.ToString()); 
1

不需要擴展方法備選答案:

var friendlyName = db.Scanners.Where(s => s.ScannerID == 24) 
        .Select(s => s.FriendlyName).FirstOrDefault() ?? "N/A"; 
+0

這只是一個選項,如果你有一個'IEnumerable'開頭。如果所討論的項目不是序列的一部分,那麼這不太可行。 – Servy

+0

這很公平。我認爲OP是專門試圖避免雙重FirstOrDefault反模式,但你是對的,這是我的一個假設。 – aquinas