简体   繁体   中英

Use .DefaultIfEmpty() instead of .FirstOrDefault() ?? String.Empty;

How can I integrate the .DefaultIfEmpty() extension method so I have not to use

.FirstOrDefault() ?? String.Empty;

Code:

(from role in roleList
let roleArray = role.RoleId.Split(new char[] { WorkflowConstants.WorkflowRoleDelimiter })
where roleArray.Length.Equals(_SplittedRoleIdArrayLength) && 
      HasAccessToCurrentUnit(roleArray[_UnitIndexInRoleId])
select roleArray[_LevelIndexInRoleId]).FirstOrDefault() ?? String.Empty;

You could use:

var query = ...;

return query.DefaultIfEmpty(string.Empty).First();

But this doesn't reduce the complexity IMO.

If you're interested in extension method then you could use something like this:

public static class Helpers
{
    public static string FirstOrEmpty(this IEnumerable<string> source)
    {
        return source.FirstOrDefault() ?? string.Empty;
    }
}

Edit

This method isn't generic because then we'd have to use default(T) and it'll give us null instead of string.Empty .

I found this course on PluralSight interesting and I remembered it when I saw this question.

You could watch the whole course, but the Map Reduce and Option<T> strategies especially with DefaultIfEmpty seemed like it could be a good fit to your use case.

Tactical Design Patterns in .NET: Control Flow by Zoran Horvat https://app.pluralsight.com/library/courses/tactical-design-patterns-dot-net-control-flow/table-of-contents

The code:

var query=(
    from role in roleList
    let delimiter=WorkflowConstants.WorkflowRoleDelimiter
    let roleArray=role.RoleId.Split(new char[] { delimiter })
    where roleArray.Length.Equals(_SplittedRoleIdArrayLength)
    where HasAccessToCurrentUnit(roleArray[_UnitIndexInRoleId])
    select roleArray[_LevelIndexInRoleId]
    ).DefaultIfEmpty("").FirstOrDefault();

For the suspicion about the semantic meaning of DefaultIfEmpty and FirstOrDefault , following is the code decompiled from the library:

  • Code

     public static IEnumerable<TSource> DefaultIfEmpty<TSource>(this IEnumerable<TSource> source) { return source.DefaultIfEmpty<TSource>(default(TSource)); } public static IEnumerable<TSource> DefaultIfEmpty<TSource>(this IEnumerable<TSource> source, TSource defaultValue) { if (source == null) { throw Error.ArgumentNull("source"); } return DefaultIfEmptyIterator<TSource>(source, defaultValue); } public static TSource First<TSource>(this IEnumerable<TSource> source) { if (source == null) { throw Error.ArgumentNull("source"); } IList<TSource> list = source as IList<TSource>; if (list != null) { if (list.Count > 0) { return list[0]; } } else { using (IEnumerator<TSource> enumerator = source.GetEnumerator()) { if (enumerator.MoveNext()) { return enumerator.Current; } } } throw Error.NoElements(); } public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source) { if (source == null) { throw Error.ArgumentNull("source"); } IList<TSource> list = source as IList<TSource>; if (list != null) { if (list.Count > 0) { return list[0]; } } else { using (IEnumerator<TSource> enumerator = source.GetEnumerator()) { if (enumerator.MoveNext()) { return enumerator.Current; } } } return default(TSource); } 

And here are something to mention:

  1. DefaultIfEmpty has a parameterless overload, which invokes the parameterized overload with default(TSource) and return its result.

  2. The only difference between parameterless FirstOrDefault and First , is the latter would throw when the collection is empty.

    For more information, see Enumerable.FirstOrDefault<TSource> Method on MSDN.

  3. FirstOrDefault semantically expressed first or default , and so called; it's not named first or null . In c#, default(T) for a reference type is null , but for non-reference type, it's not. For example, default(int) is zero.

    The keyword default was never said null semantically. It's DEFAULT .

    Also, for more information, default Keyword on MSDN.

.NET 6 introduces the concept of being able to pass in what the default value should be, should the item not be found. So for example, this code is now valid in .NET 6:

source.FirstOrDefault(String.Empty);

Or:

List<int> numbers = new List<int>();
var custom = numbers.FirstOrDefault(-1);

In the example above we don't need to be reliant on getting back 0 which is the default for int anymore. This could be handy when 0 is a valid number in the context of using the list of numbers where as -1 isn't.

You can read more:

https://dotnetcoretutorials.com/2021/09/02/linq-ordefault-enhancements-in-net-6/ https://adamstorr.azurewebsites.net/blog/default-your-firstordefault-in-net6.0

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM