我正在从linq表达式生成一个十进制值列表,我想要最小的非零值.但是,linq表达式完全有可能导致空列表.
这将引发异常并且没有MinOrDefault来应对这种情况.
decimal result = (from Item itm in itemList
where itm.Amount > 0
select itm.Amount).Min();
Run Code Online (Sandbox Code Playgroud)
如果列表为空,将结果设置为0的最佳方法是什么?
Chr*_*tte 119
你想要的是这个:
IEnumerable<double> results = ... your query ...
double result = results.MinOrDefault();
Run Code Online (Sandbox Code Playgroud)
好吧,MinOrDefault()不存在.但是如果我们自己实现它会看起来像这样:
public static class EnumerableExtensions
{
public static T MinOrDefault<T>(this IEnumerable<T> sequence)
{
if (sequence.Any())
{
return sequence.Min();
}
else
{
return default(T);
}
}
}
Run Code Online (Sandbox Code Playgroud)
但是,有一些功能System.Linq会产生相同的结果(以稍微不同的方式):
double result = results.DefaultIfEmpty().Min();
Run Code Online (Sandbox Code Playgroud)
如果results序列不包含任何元素,DefaultIfEmpty()则会产生一个包含一个元素的序列default(T)- 您随后可以调用该元素Min().
如果default(T)不是您想要的,那么您可以指定自己的默认值:
double myDefault = ...
double result = results.DefaultIfEmpty(myDefault).Min();
Run Code Online (Sandbox Code Playgroud)
现在,这很整洁!
Mar*_*ell 52
decimal? result = (from Item itm in itemList
where itm.Amount != 0
select (decimal?)itm.Amount).Min();
Run Code Online (Sandbox Code Playgroud)
请注意转换为decimal?.如果没有,你会得到一个空的结果(只是在事后处理 - 我主要说明如何停止异常).我也做了"非零"使用!=而不是>.
Jon*_*nna 10
正如已经提到的那样,在少量代码中执行一次最好的方法是:
decimal result = (from Item itm in itemList
where itm.Amount > 0
select itm.Amount).DefaultIfEmpty().Min();
Run Code Online (Sandbox Code Playgroud)
随着铸造itm.Amount到decimal?并获得Min那是最整洁的,如果我们希望能够检测到这种空状态.
如果你想要实际提供一个,MinOrDefault()那么我们当然可以从:
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);
}
Run Code Online (Sandbox Code Playgroud)
您现在拥有一整套MinOrDefault是否包含选择器,以及是否指定默认值.
从这一点开始,您的代码就是:
decimal result = (from Item itm in itemList
where itm.Amount > 0
select itm.Amount).MinOrDefault();
Run Code Online (Sandbox Code Playgroud)
所以,虽然它不是那么开始,但从那时起它就更整洁了.
可是等等!还有更多!
假设您使用EF并希望使用async支持.轻松完成:
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);
}
Run Code Online (Sandbox Code Playgroud)
(注意我不在await这里使用;我们可以直接创建一个Task<TSource>没有它我们需要的东西,从而避免隐藏的复杂性await带来).
但等等,还有更多!假设我们IEnumerable<T>有时会使用它.我们的方法是次优的.当然,我们可以做得更好!
首先,Min定义上int?,long?,float? double?和decimal?已经做我们想做(就像马克Gravell的答案利用的).同样,我们也从Min已定义的行为中获取我们想要的行为,如果被调用的话T?.因此,让我们做一些小的,因此很容易内联的方法来利用这个事实:
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);
}
Run Code Online (Sandbox Code Playgroud)
现在让我们先从更一般的案例开始:
public static TSource MinOrDefault<TSource>(this IEnumerable<TSource> source, TSource defaultValue)
{
if(default(TSource) == null) //Nullable type. Min already copes with empty sequences
{
//Note that the jitter generally removes this code completely when `TSource` is not nullable.
var result = source.Min();
return result == null ? defaultValue : result;
}
else
{
//Note that the jitter generally removes this code completely when `TSource` is nullable.
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;
}
Run Code Online (Sandbox Code Playgroud)
现在显而易见的覆盖使用了这个:
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();
}
Run Code Online (Sandbox Code Playgroud)
如果我们真的看好性能,我们可以针对某些情况进行优化,就像这样Enumerable.Min():
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();
}
Run Code Online (Sandbox Code Playgroud)
等了long,float,double和decimal相匹配的一套Min()提供Enumerable.这是T4模板很有用的东西.
最后MinOrDefault(),对于各种类型的我们来说,我们几乎可以实现我们所希望的实施.面对一次使用它当然不是"整洁"(再次,只是使用DefaultIfEmpty().Min()),但如果我们发现自己经常使用它,那么非常"整洁",所以我们有一个很好的库我们可以重用(或者实际上,粘贴到StackOverflow上的答案......).