Linq에서“MinOrDefault”를 달성하는 가장 좋은 방법은 무엇입니까?
linq 식에서 10 진수 값 목록을 생성하고 있으며 0이 아닌 최소값을 원합니다. 그러나 linq 표현식이 빈 목록을 생성하는 것은 전적으로 가능합니다.
이 경우 예외가 발생하며이 상황에 대처할 수있는 MinOrDefault가 없습니다.
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();
로 변환합니다 decimal?
. 아무것도 없으면 빈 결과를 얻습니다 (사실 후에 처리하십시오-주로 예외를 중지하는 방법을 설명하고 있습니다). 또한 "비 제로"사용했다 !=
보다는 >
.
원하는 것은 다음과 같습니다.
IEnumerable<double> results = ... your query ...
double result = results.MinOrDefault();
글쎄, MinOrDefault()
존재하지 않습니다. 그러나 우리가 직접 구현한다면 다음과 같이 보일 것입니다.
public static class EnumerableExtensions
{
public static T MinOrDefault<T>(this IEnumerable<T> sequence)
{
if (sequence.Any())
{
return sequence.Min();
}
else
{
return default(T);
}
}
}
그러나 System.Linq
동일한 결과를 생성하는 기능이 있습니다 (약간 다른 방식으로).
double result = results.DefaultIfEmpty().Min();
경우 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
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);
}
이제 MinOrDefault
선택기 포함 여부 및 기본값 지정 여부에 대한 전체 세트가 있습니다.
이 시점에서 코드는 간단합니다.
decimal result = (from Item itm in itemList
where itm.Amount > 0
select itm.Amount).MinOrDefault();
따라서 처음에는 깔끔하지는 않지만 그때부터는 깔끔합니다.
하지만 기다려! 더있다!
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);
}
( await
여기서는 사용하지 않습니다 . 우리 Task<TSource>
가 필요로 하는 것을 직접 만들 수 있으므로 숨겨진 합병증을 피할 수 있습니다 await
.)
하지만 더 있습니다! 이것을 IEnumerable<T>
몇 번 사용한다고 가정 해 봅시다 . 우리의 접근 방식은 차선책입니다. 확실히 우리는 더 잘할 수 있습니다!
첫째, Min
정의 int?
, long?
, float?
double?
그리고 decimal?
이미 (마크 Gravell의 응답 차종이의 사용으로) 우리는 어쨌든 원하는 일을. 마찬가지로, Min
다른 .NET Framework를 호출 하면 이미 정의 된 에서 원하는 동작을 얻습니다 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);
}
이제 좀 더 일반적인 경우부터 시작하겠습니다.
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();
}
성능에 대해 정말로 낙관적이라면 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();
}
그래서 동안 long
, float
, double
및 decimal
세트에 맞게 Min()
에서 제공을 Enumerable
. 이것은 T4 템플릿이 유용한 종류입니다.
At the end of all that, we have just about as performant an implementation of MinOrDefault()
as we could hope for, for a wide range of types. Certainly not "neat" in the face of one use for it (again, just use DefaultIfEmpty().Min()
), but very much "neat" if we find ourselves using it a lot, so we have a nice library we can reuse (or indeed, paste into answers on StackOverflow…).
This approach will return the single smallest Amount
value from itemList
. In theory this should avoid multiple round trips to the database.
decimal? result = (from Item itm in itemList
where itm.Amount > 0)
.Min(itm => (decimal?)itm.Amount);
The null reference exception is no longer caused because we are using a nullable type.
By avoiding the use of executing methods such as Any
before calling Min
, we should only be making one trip to the database
참고URL : https://stackoverflow.com/questions/2165605/whats-the-neatest-way-to-achieve-minordefault-in-linq
'IT TIP' 카테고리의 다른 글
두 시간 사이에 몇 분이 있는지 계산 (0) | 2020.10.20 |
---|---|
D3로 SVG 요소 Z- 색인 업데이트 (0) | 2020.10.20 |
C #의 URL Slugify 알고리즘? (0) | 2020.10.20 |
웹 브라우저에 PDF 표시 (0) | 2020.10.20 |
파일이 링크되는 아키텍처가 아닌 아카이브 용으로 빌드되었습니다 (i386). (0) | 2020.10.20 |