IT story

FirstOrDefault : null 이외의 기본값

hot-time 2020. 7. 2. 07:36
반응형

FirstOrDefault : null 이외의 기본값


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


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

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()합니다.


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

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

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

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

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

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


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

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

FirstOrDefault 문서에서

소스가 비어있는 경우 [Returns] default (TSource);

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

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

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


@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)),
    };

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

    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!


Actually, I use two approaches to avoid NullReferenceException when I'm working with collections:

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;
}

For C# 6.0 or later:

Use ?. or ?[ to test if is null before perform a member access Null-conditional Operators documentation

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

C# older version:

Use DefaultIfEmpty() to retrieve a default value if the sequence is empty.MSDN Documentation

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


I just had a similar situation and was looking for a solution that allows me to return an alternative default value without taking care of it at the caller side every time I need it. What we usually do in case Linq does not support what we want, is to write a new extension that takes care of it. That´s what I did. Here is what I came up with (not tested though):

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);
    }
}

Instead of YourCollection.FirstOrDefault(), you could use YourCollection.DefaultIfEmpty(YourDefault).First() for example.


I know its been a while but Ill add to this, based on the most popular answer but with a little extension Id like to share the below:

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();
    }
}

This allows me to call it inline as such with my own example I was having issues with:

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

So for me I just wanted a default resolver to be used inline, I can do my usual check and then pass in a function so a class isn't instantiated even if unused, its a function to execute when required instead!


Use DefaultIfEmpty() instead of FirstOrDefault().

참고URL : https://stackoverflow.com/questions/12972295/firstordefault-default-value-other-than-null

반응형