C# 如何实现;MinOrDefault";在林克?
我正在从linq表达式生成一个十进制值列表,我想要最小的非零值。但是,linq表达式完全有可能导致空列表 这将引发一个异常,没有任何错误可以应对这种情况C# 如何实现;MinOrDefault";在林克?,c#,linq,C#,Linq,我正在从linq表达式生成一个十进制值列表,我想要最小的非零值。但是,linq表达式完全有可能导致空列表 这将引发一个异常,没有任何错误可以应对这种情况 decimal result = (from Item itm in itemList where itm.Amount > 0 select itm.Amount).Min(); 如果列表为空,如何将结果设置为0 decimal? result = (from I
decimal result = (from Item itm in itemList
where itm.Amount > 0
select itm.Amount).Min();
如果列表为空,如何将结果设置为0
decimal? result = (from Item itm in itemList
where itm.Amount != 0
select (decimal?)itm.Amount).Min();
请注意转换为十进制?
。如果没有异常,您将得到一个空结果(只需在事后处理它-我主要说明如何停止异常)。我还使用了“非零”=
而不是
您想要的是:
IEnumerable<double> results = ... your query ...
double result = results.MinOrDefault();
但是,System.Linq
中有一些功能将产生相同的结果(以稍微不同的方式):
如果results
序列不包含任何元素,DefaultIfEmpty()
将生成一个包含一个元素的序列,即default(T)
,您随后可以调用Min()
如果default(T)
不是您想要的,那么您可以使用以下命令指定自己的默认值:
double myDefault = ...
double result = results.DefaultIfEmpty(myDefault).Min();
现在,这太好了 此方法将从
项目列表
返回单个最小的金额
值。理论上,这应该避免多次往返数据库
decimal? result = (from Item itm in itemList
where itm.Amount > 0)
.Min(itm => (decimal?)itm.Amount);
由于我们使用的是可为null的类型,因此不再导致null引用异常
通过在调用
Min
之前避免使用执行方法,例如Any
,我们应该只访问一次数据库,就少量代码只执行一次而言,最简单的方法是,如前所述:
decimal result = (from Item itm in itemList
where itm.Amount > 0
select itm.Amount).DefaultIfEmpty().Min();
将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);
}
所以,虽然一开始就不那么整洁,但从那时起就更整洁了
但是等等!还有更多
假设您使用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);
}
现在让我们先从更一般的情况开始:
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;
}
依此类推,long
、float
、double
和decimal
,以匹配可枚举
提供的Min()
集合。这就是T4模板有用的地方
最后,对于各种类型,我们的
MinOrDefault()
实现的性能与我们所希望的一样。在一次使用它时当然不是“整洁”的(同样,只使用DefaultIfEmpty().Min()
),但是如果我们发现自己经常使用它,那么我们就非常“整洁”,因此我们有一个很好的库可以重用(或者实际上,在StackOverflow上粘贴到答案中…。如果itemList不可为null(其中DefaultIfEmpty给出0)如果希望null作为潜在输出值,也可以使用lambda语法:
decimal? result = itemList.Where(x => x.Amount != 0).Min(x => (decimal?)x);
有趣。我不知道这将如何避免一个空列表,但我会尝试一下:
decimal?结果=(新小数?[0]).Min()代码>给出null
,然后可能使用??0以获得所需的结果?它肯定有效。我刚刚构建了一个单元测试来进行测试,但是我需要花5分钟的时间来弄清楚为什么select的结果是一个空值而不是一个空列表(我的sql背景可能让我感到困惑)。谢谢。@Lette,如果我将其更改为:decimal result1=……Min()??0; 这也行得通,所以谢谢你的输入。@Christofferette我只想要一个T的空列表,所以我也使用了Any()和Min()。谢谢@AdRiangMar:BTW,您是否考虑使用A作为默认值?这里提到的MyOrrRebug实现将迭代枚举两次。这对于内存中的集合并不重要,但对于LINQ to Entity或惰性“yield return”构建的可枚举项,这意味着两次往返数据库或两次处理第一个元素。我更喜欢results.DefaultIfEmpty(myDefault).Min()解决方案。查看DefaultIfEmpty
的源代码,它确实实现得很智能,仅当存在使用的元素时,序列的转发才会返回s.@jAndChips,您引用的格式是DefaultIfEmpty
,该格式采用IEnumerable
。如果在IQueryable
上调用它,例如在数据库操作中调用它,那么它不会返回单例序列,而是生成适当的MethodCallExpression
,因此结果查询不需要检索所有内容。这里建议的EnumerableExtensions
方法确实存在这个问题。您怎么会认为在接受的答案中使用Select
会多次执行查询?接受的答案将导致一个DB调用。没错,Select
是一个延迟方法,不会导致执行。我已经把这些谎言从我的回答中去掉了。参考资料:亚当·弗里曼(Adam Freeman)的《Pro ASP.NET MVC4》如果你真的想在确保没有浪费方面保持乐观,请看我刚刚发布的答案。
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);
}
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);
}
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;
}
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();
}
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();
}
decimal? result = itemList.Where(x => x.Amount != 0).Min(x => (decimal?)x);
decimal result;
try{
result = (from Item itm in itemList
where itm.Amount != 0
select (decimal?)itm.Amount).Min();
}catch(Exception e){
result = 0;
}