Thursday, December 2, 2010

C# Puzzle No.22 (beginner)

The .FirstOrDefault(…) LINQ’s method is quite handy until you realize that “default” in fact means default(T) which then means “null” if you deal with reference types. This could cause problems if you expect anything but null.

class Program
{
    static void Main( string[] args )
    {
        List<User> Users = 
             new List<User>() { new User() { Name = "u1" } };
 
        var name = 
            Users
                .FirstOrDefault( u => u.Name == "u2" )
                // boom! FirstOrDefault returns null
                .Name;
 
        Console.WriteLine( name );
        Console.ReadLine();
    }
}
 
class User
{
    public string Name;
}

What if you have a Null Object you’d like to provide as the “default” instead of default(T)?

class User
{
    public string Name;
 
    public static User NullValue
    {
        get
        {
            return new User() { Name = "NULL" };
        }
    }
}

The easiest workaround is to add an additional, overloaded version of FirstOrDefault which could accept a reference to an object which should be returned as the “default”:

class Program
{
    static void Main( string[] args )
    {
        List<User> Users = new List<User>() { new User() { Name = "u1" } };
 
        var name = 
            Users
                // use new, overloaded FirstOrDefault implemented below
                .FirstOrDefault( u => u.Name == "u2", User.NullValue )
                .Name;
 
        Console.WriteLine( name );
        Console.ReadLine();
    }
}
 
public static class LinqExtension
{
    public static T FirstOrDefault<T>( 
        this IEnumerable<T> Entites, 
        Func<T, 
        bool> Predicate, 
        T Default )
    {
        var entity = Entites.FirstOrDefault( Predicate );
 
        // return provided Default if there are no matching entities
        if ( (object)entity != (object)default( T ) ) 
            return entity;
        else
            return Default;
    }
}
 
class User
{
    public string Name;
 
    public static User NullValue
    {
        get
        {
            return new User() { Name = "NULL" };
        }
    }
}

Your goal is to achieve the same effect but with no new extension methods. In other words – you are restricted to the standard set of LINQ operators.

class Program
{
    static void Main( string[] args )
    {
        List<User> Users = new List<User>() { new User() { Name = "u1" } };
 
        var name = 
            Users
                // linq query here which returns the matching object
                // or User.NullValue as the "default"
                // so that this field reference below always succeeds 
                .Name;
 
        Console.WriteLine( name );
        Console.ReadLine();
    }
}
 
class User
{
    public string Name;
 
    public static User NullValue
    {
        get
        {
            return new User() { Name = "NULL" };
        }
    }
}

3 comments:

Omer Mor said...

.Where( u => u.Name == "u2" )
.Concat( User.NullValue )
.First()

pawlos said...

You can also use DefaultIfEmpty.

.Where(u => u.Name == "u2")
.DefaultIfEmpty(User.NullValue)
.First()

Wiktor Zychla said...

great, both answers are correct :)