Az miktarda bir kodda sadece bir kez yapmak açısından en düzgün olanı, daha önce de belirtildiği gibi:
decimal result = (from Item itm in itemList
where itm.Amount > 0
select itm.Amount).DefaultIfEmpty().Min();
Döküm ile itm.Amount
karşı decimal?
ve elde Min
nedenle biz bu boş durumu tespit edebilmek istiyorsanız etkileyicisi olmak.
Bununla birlikte, gerçekten bir sağlamak istiyorsanız, MinOrDefault()
o zaman elbette şununla başlayabiliriz:
public static TSource MinOrDefault<TSource>(this IQueryable<TSource> source, TSource defaultValue)
{
return source.DefaultIfEmpty(defaultValue).Min();
}
public static TSource MinOrDefault<TSource>(this IQueryable<TSource> source)
{
return source.DefaultIfEmpty(defaultValue).Min();
}
public static TResult MinOrDefault<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector, TSource defaultValue)
{
return source.DefaultIfEmpty(defaultValue).Min(selector);
}
public static TResult MinOrDefault<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector)
{
return source.DefaultIfEmpty().Min(selector);
}
Artık MinOrDefault
bir seçici ekleyip eklemediğinize ve varsayılanı belirleyip belirlemediğinize dair tam bir setiniz var .
Bu noktadan itibaren kodunuz basitçe:
decimal result = (from Item itm in itemList
where itm.Amount > 0
select itm.Amount).MinOrDefault();
Yani, başlamak kadar düzgün olmasa da, o andan itibaren daha temiz.
Fakat bekle! Fazlası var!
Diyelim ki EF kullanıyorsunuz ve async
destekten yararlanmak istiyorsunuz . Kolayca yapılır:
public static Task<TSource> MinOrDefaultAsync<TSource>(this IQueryable<TSource> source, TSource defaultValue)
{
return source.DefaultIfEmpty(defaultValue).MinAsync();
}
public static Task<TSource> MinOrDefaultAsync<TSource>(this IQueryable<TSource> source)
{
return source.DefaultIfEmpty(defaultValue).MinAsync();
}
public static Task<TSource> MinOrDefaultAsync<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector, TSource defaultValue)
{
return source.DefaultIfEmpty(defaultValue).MinAsync(selector);
}
public static Task<TSource> MinOrDefaultAsync<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector)
{
return source.DefaultIfEmpty().MinAsync(selector);
}
( await
Burada kullanmadığımı unutmayın ; Task<TSource>
ihtiyacımız olanı onsuz yapan ve dolayısıyla await
getirdiği gizli komplikasyonlardan kaçınan bir doğrudan yaratabiliriz ).
Ama bekleyin, dahası var! Diyelim ki bunu IEnumerable<T>
bazen kullanıyoruz. Yaklaşımımız yetersizdir. Elbette daha iyisini yapabiliriz!
İlk olarak, Min
tanımlanmış int?
, long?
, float?
double?
ve decimal?
zaten (Marc Gravell cevabı markaları kullanımı gibi) biz yine istediğimi yapıyorum. Benzer şekilde, Min
başka biri için çağrılırsa , önceden tanımlanmış olandan da istediğimiz davranışı elde ederiz T?
. Öyleyse, bu gerçeğin avantajlarından yararlanmak için bazı küçük ve dolayısıyla kolayca satır içine alınmış yöntemler yapalım:
public static TSource? MinOrDefault<TSource>(this IEnumerable<TSource?> source, TSource? defaultValue) where TSource : struct
{
return source.Min() ?? defaultValue;
}
public static TSource? MinOrDefault<TSource>(this IEnumerable<TSource?> source) where TSource : struct
{
return source.Min();
}
public static TResult? Min<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult?> selector, TResult? defaultValue) where TResult : struct
{
return source.Min(selector) ?? defaultValue;
}
public static TResult? Min<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult?> selector) where TResult : struct
{
return source.Min(selector);
}
Şimdi önce daha genel bir durumla başlayalım:
public static TSource MinOrDefault<TSource>(this IEnumerable<TSource> source, TSource defaultValue)
{
if(default(TSource) == null)
{
var result = source.Min();
return result == null ? defaultValue : result;
}
else
{
var comparer = Comparer<TSource>.Default;
using(var en = source.GetEnumerator())
if(en.MoveNext())
{
var currentMin = en.Current;
while(en.MoveNext())
{
var current = en.Current;
if(comparer.Compare(current, currentMin) < 0)
currentMin = current;
}
return currentMin;
}
}
return defaultValue;
}
Şimdi bundan yararlanan bariz geçersiz kılmalar:
public static TSource MinOrDefault<TSource>(this IEnumerable<TSource> source)
{
var defaultValue = default(TSource);
return defaultValue == null ? source.Min() : source.MinOrDefault(defaultValue);
}
public static TResult MinOrDefault<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector, TResult defaultValue)
{
return source.Select(selector).MinOrDefault(defaultValue);
}
public static TResult MinOrDefault<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
return source.Select(selector).MinOrDefault();
}
Performans konusunda gerçekten iyimser isek, tıpkı Enumerable.Min()
yaptığı gibi belirli durumlar için optimizasyon yapabiliriz :
public static int MinOrDefault(this IEnumerable<int> source, int defaultValue)
{
using(var en = source.GetEnumerator())
if(en.MoveNext())
{
var currentMin = en.Current;
while(en.MoveNext())
{
var current = en.Current;
if(current < currentMin)
currentMin = current;
}
return currentMin;
}
return defaultValue;
}
public static int MinOrDefault(this IEnumerable<int> source)
{
return source.MinOrDefault(0);
}
public static int MinOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector, int defaultValue)
{
return source.Select(selector).MinOrDefault(defaultValue);
}
public static int MinOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector)
{
return source.Select(selector).MinOrDefault();
}
Ve böylece için üzerinde long
, float
, double
ve decimal
kümesini maç için Min()
sağladığı Enumerable
. Bu, T4 şablonlarının yararlı olduğu türden bir şeydir.
Tüm bunların sonunda, MinOrDefault()
çok çeşitli türler için umduğumuz kadar performanslı bir uygulamamız var . Kesinlikle tek bir kullanım karşısında "temiz" değil (yine, sadece kullanın DefaultIfEmpty().Min()
), ama onu çok kullanırken bulursak çok "temiz", yani yeniden kullanabileceğimiz (veya gerçekten yapıştırabileceğimiz güzel bir kitaplığımız var) StackOverflow'daki yanıtlar…).