FirstOrDefault : null 이외의 기본값


142

내가 이해하는 것처럼 Linq에서 메소드 FirstOrDefault()Defaultnull 이외 의 값을 반환 할 수 있습니다 . 내가 해결하지 않은 것은 쿼리 결과에 항목이 없을 때 null (이와 비슷한) 메소드로 null 이외의 다른 유형을 반환 할 수 있다는 것입니다. 특정 쿼리에 대한 값이없는 경우 미리 정의 된 일부 값이 기본값으로 반환되도록 설정할 수있는 특정 방법이 있습니까?


147
대신에 예를 들어 YourCollection.FirstOrDefault()사용할 수 있습니다 YourCollection.DefaultIfEmpty(YourDefault).First().
나무 늘보

6
나는 위의 의견과 같은 것을 꽤 오랫동안 찾고 있었고, 그것은 엄청나게 도움이되었습니다. 이것이 정답입니다.
Brandon

위의 의견이 가장 좋습니다.
Tom Padilla

필자의 경우 반환 된 값이 nullable이고 nullable이 아닌 값에 할당 된 경우 @ sloth 답변이 작동하지 않았습니다. 나는 그것을 위해 사용 MyCollection.Last().GetValueOrDefault(0)했다. 그렇지 않으면 아래 @Jon Skeet 답변이 IMO가 정확합니다.
Jnr

답변:


46

값 유형이 아닌 일반적인 경우 :

static class ExtensionsThatWillAppearOnEverything
{
    public static T IfDefaultGiveMe<T>(this T value, T alternate)
    {
        if (value.Equals(default(T))) return alternate;
        return value;
    }
}

var result = query.FirstOrDefault().IfDefaultGiveMe(otherDefaultValue);

다시 말하지만, 시퀀스 어떤 것이 있는지 또는 첫 번째 값이 기본값 인지 알 수 없습니다 .

당신이 이것에 관심이 있다면, 당신은 같은 것을 할 수 있습니다

static class ExtensionsThatWillAppearOnIEnumerables
{
    public static T FirstOr<T>(this IEnumerable<T> source, T alternate)
    {
        foreach(T t in source)
            return t;
        return alternate;
    }
}

로 사용

var result = query.FirstOr(otherDefaultValue);

Steak 씨가 지적했듯이 이것은 이것에 의해서도 가능 .DefaultIfEmpty(...).First()합니다.


일반적인 방법 <T>에는 이름이 필요 하지만 더 심각한 방법은 value == default(T)작동하지 않는 것입니다 ( T평등을 비교할 수 있는지 아는 사람이 있습니까?)
AakashM

지적 해 주셔서 감사합니다. @AakashM; 나는 실제로 이것을 시도했고 그것이 가치가 있어야한다고 생각하지만 (값 유형의 권투는 좋아하지 않지만).
Rawling

3
@Rawling EqualityComparer<T>.Default.Equals(value, default(T))복싱을 피하고 값이 다음과 같은 경우 예외를 피하는 데 사용 합니다.null
Lukazoid

199

내가 이해하는 것처럼 Linq에서 FirstOrDefault () 메소드는 null 이외의 기본값을 반환 할 수 있습니다.

또는 요소 유형의 기본값을 항상 리턴합니다. 이는 널 참조, 널 입력 가능 값 유형의 널 (NULL) 값 또는 널 입력 불가능 값 유형의 자연 "모든 0"값입니다.

특정 쿼리에 대한 값이없는 경우 미리 정의 된 일부 값이 기본값으로 반환되도록 설정할 수있는 특정 방법이 있습니까?

참조 유형의 경우 다음을 사용할 수 있습니다.

var result = query.FirstOrDefault() ?? otherDefaultValue;

물론 첫 번째 값이 존재하지만 null 참조 인 경우 "기타 기본값" 제공합니다 ...


질문에 참조 유형이 필요하지만 요소가과 같은 값 유형 인 경우 솔루션이 작동하지 않는다는 것을 알고 있습니다 int. 나는 많은의 사용을 선호 DefaultIfEmpty: src.Where(filter).DefaultIfEmpty(defaultValue).First(). 값 유형과 참조 유형 모두에서 작동합니다.
KFL

@ KFL : nullable이 아닌 값 유형의 경우 아마도 그것을 사용할 것입니다. 그러나 nullable 경우에는 더 오래갑니다.
Jon Skeet

반환 형식을 통해 최고 제어 기본값은 null의 경우 ... :)
Sundara Prabu

"아니요. 또는 항상 요소 유형의 기본값을 반환합니다 ..."-실제로 해냈습니다. 필요한 경우 기본값을 제공 할 수 있다고 가정하여 함수 이름의 의미를 잘못 이해 했으므로
Jesus Campon

이 방법이 정말 마음에 들었지만 "Func <T> alternate"로 "T alternate"를 변경 한 다음 "return alternate ();" 그런 식으로 내가 필요하지 않으면 여분의 객체를 생성하지 않습니다. 함수가 행에서 여러 번 호출되는 경우 특히 유용합니다. 생성자가 느리거나 유형의 대체 인스턴스가 많은 메모리를 차지합니다.
Dan Violet Sagmiller

64

DefaultIfEmpty 다음에 First를 사용할 수 있습니다 .

T customDefault = ...;
IEnumerable<T> mySequence = ...;
mySequence.DefaultIfEmpty(customDefault).First();

나는의 아이디어를 사랑 DefaultIfEmpty- 그것은 기본이 지정 될 필요가 모든 API와 함께 작동 : First(), Last(), 등의 사용자로서, 당신이하지 않는 기본을 지정할 수 있도록하는 API를 기억할 필요가 없습니다. 매우 우아합니다!
KFL

이것은 많은 둥지 답입니다.
Jesse Williams

19

FirstOrDefault 문서에서

소스가 비어 있으면 [Returns] default (TSource);

default (T)에 대한 문서에서 :

기본 키워드. 참조 유형의 경우 null을, 숫자 값 유형의 경우 0을 반환합니다. 구조체의 경우 구조체의 값 또는 참조 유형에 따라 0 또는 null로 초기화 된 구조체의 각 멤버를 반환합니다. nullable 값 형식의 경우 default는 모든 구조체와 같이 초기화되는 System.Nullable을 반환합니다.

따라서 유형이 참조인지 또는 값 유형인지에 따라 기본값은 null 또는 0 일 수 있지만 기본 동작은 제어 할 수 없습니다.


6

@sloth의 코멘트에서 복사 함

대신에 예를 들어 YourCollection.FirstOrDefault()사용할 수 있습니다 YourCollection.DefaultIfEmpty(YourDefault).First().

예:

var viewModel = new CustomerDetailsViewModel
    {
            MainResidenceAddressSection = (MainResidenceAddressSection)addresses.DefaultIfEmpty(new MainResidenceAddressSection()).FirstOrDefault( o => o is MainResidenceAddressSection),
            RiskAddressSection = addresses.DefaultIfEmpty(new RiskAddressSection()).FirstOrDefault(o => !(o is MainResidenceAddressSection)),
    };

2
DefaultIfEmpty컬렉션이 비어있는 경우 (0 개의 항목이있는 경우) 기본 값 을 반환합니다. First예제에서와 같이 WITH 표현식 을 사용 하고 해당 조건에서 항목을 찾지 못하면 반환 값이 비어 있습니다.
OriolBG

5

당신은 또한 이것을 할 수 있습니다

    Band[] objects = { new Band { Name = "Iron Maiden" } };
    first = objects.Where(o => o.Name == "Slayer")
        .DefaultIfEmpty(new Band { Name = "Black Sabbath" })
        .FirstOrDefault();   // returns "Black Sabbath" 

이것은 linq만을 사용합니다-yipee!


2
이 답변과 비타민 C의 답변의 유일한 차이점은이 FirstOrDefault대신을 사용 한다는 것입니다 First. msdn.microsoft.com/en-us/library/bb340482.aspx 에 따르면 권장되는 사용법은First
Daniel

5

실제로 NullReferenceException컬렉션 을 사용할 때 피하기 위해 두 가지 접근 방식을 사용합니다 .

public class Foo
{
    public string Bar{get; set;}
}
void Main()
{
    var list = new List<Foo>();
    //before C# 6.0
    string barCSharp5 = list.DefaultIfEmpty(new Foo()).FirstOrDefault().Bar;
    //C# 6.0 or later
    var barCSharp6 = list.FirstOrDefault()?.Bar;
}

C # 6.0 이상의 경우 :

사용 ?.또는 ?[테스트에 널 (null)이 전 멤버 액세스를 수행하는 경우 널 조건부 연산자 문서

예: var barCSharp6 = list.FirstOrDefault()?.Bar;

C # 이전 버전 :

DefaultIfEmpty()시퀀스가 비어있는 경우 기본값을 검색하는 데 사용 합니다. MSDN 설명서

예: string barCSharp5 = list.DefaultIfEmpty(new Foo()).FirstOrDefault().Bar;


1
표현식 트리 람바에서는 널 전파 연산자를 사용할 수 없습니다.
Lars335

1

대신에 예를 들어 YourCollection.FirstOrDefault()사용할 수 있습니다 YourCollection.DefaultIfEmpty(YourDefault).First().


도움이되었다고 생각되면 공감할 수 있습니다. 이것은 답이 아닙니다.
jannagy02

1

방금 비슷한 상황이 발생하여 필요할 때마다 호출자 측에서 돌보지 않고 대체 기본값을 반환 할 수있는 솔루션을 찾고있었습니다. Linq가 원하는 것을 지원하지 않는 경우 일반적으로하는 일은 그것을 처리하는 새로운 확장을 작성하는 것입니다. 그게 내가 한 일이야 다음은 내가 생각해 낸 것입니다 (테스트되지는 않음).

public static class EnumerableExtensions
{
    public static T FirstOrDefault<T>(this IEnumerable<T> items, T defaultValue)
    {
        foreach (var item in items)
        {
            return item;
        }
        return defaultValue;
    }

    public static T FirstOrDefault<T>(this IEnumerable<T> items, Func<T, bool> predicate, T defaultValue)
    {
        return items.Where(predicate).FirstOrDefault(defaultValue);
    }

    public static T LastOrDefault<T>(this IEnumerable<T> items, T defaultValue)
    {
        return items.Reverse().FirstOrDefault(defaultValue);
    }

    public static T LastOrDefault<T>(this IEnumerable<T> items, Func<T, bool> predicate, T defaultValue)
    {
        return items.Where(predicate).LastOrDefault(defaultValue);
    }
}

0

나는 그것이 오래되었다는 것을 알고 있지만 가장 인기있는 답변을 기반으로하지만 이것에 추가 할 것입니다.

static class ExtensionsThatWillAppearOnIEnumerables
{
    public static T FirstOr<T>(this IEnumerable<T> source, Func<T, bool> predicate, Func<T> alternate)
    {
        var thing = source.FirstOrDefault(predicate);
        if (thing != null)
            return thing;
        return alternate();
    }
}

이렇게하면 문제가 발생한 내 예제와 같이 인라인으로 호출 할 수 있습니다.

_controlDataResolvers.FirstOr(x => x.AppliesTo(item.Key), () => newDefaultResolver()).GetDataAsync(conn, item.ToList())

그래서 나는 기본 리졸버가 인라인으로 사용되기를 원했습니다. 평소의 확인을 수행 한 다음 함수를 전달하여 클래스가 사용되지 않더라도 인스턴스화되지 않고 대신 필요할 때 실행되도록 함수를 전달할 수 있습니다!


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.