Tech Off Thread

5 posts

Forum Read Only

This forum has been made read only by the site admins. No new threads or comments can be added.

Method shootout: Which is better?

Back to Forum: Tech Off
  • User profile image
    exoteric

    I was making a little parsing library today for work and came up with a simple extension method - but since then I've thought about a variation. Now I'm a little perplexed as to which is better. There are pros and cons to both.

     

    The first idea:

     

    public static T? FirstOrNull<T>(this IEnumerable<T> source) where T : struct
    {
        Contract.Requires(source != null);
    
        return source.Any() ? source.First() : (T?)null;
    }

     

    The idea is that for struct types it's not desirable to have the default value because you can't tell whether the value returned represents a missing value or just happens to coincide with the default for that type. This method mitigates that.

     

    Now, I added the simple null check at the beginning but then started working on ElementAt and other methods but this lead to the thought: even if the sequence itself is null we can sort of represent that too with the null return value: so if the sequence is empty or null, then we return a null. Here's how that code looks like:

     

    public static T? FirstOrNull<T>(this IEnumerable<T> source) where T : struct
    {
        return source == null || !source.Any() ? (T?)null : source.First();
    }

     

    I like that the function is a total function: it deals with all cases in a quasi-meaningful way. On the other side, you can't tell whether a null return value signifies an "out of bounds" exceptional value or a "source is null" exceptional value.

     

    In a way the second method introduces a problem that the first one fixes (ability to infer something about the source from the return value alone). On the other hand it seamlessly handles any value thrown at it.

  • User profile image
    Dexter

    I'd say that the first variant is preferable because its behavior is similar with the existing IEnumerable<T> extensions. It's also consistent with the way normal (non extension) methods work, that is, they don't work on a null instance.

    In addition I have a personal dislike for null IEnumerable, IList, array etc. I prefer empty "collections" whever possible.

  • User profile image
    exoteric

    Your distaste for nullness is shared but since we can't express non-nullness for reference types* (e.g. IEnumerable! xs), we have to deal with the cards we are dealt: the possible odd null value. But you are right that the second version is unorthodox and perhaps undesirable or an anti-pattern; it does have the elegance of totality though: no exceptions, it'll handle everything thrown at it in a graceful way.

     * In C#

     

  • User profile image
    Adam​Speight2008

    If the enumerable is empty would it match

    Enumerable.Empty<T>

     

    It could be worth it raising the level of indirection.

    Return New Nulllable<T>() // Has No value
     

    Return New Nullable<T>(Source.First()) // First Element

     

    Now every where the return values is used you have to check its null-ness.

    rv=FirstOrNull
    if( rv.HasValue){ }; 

     

     

    Or return a Func<T>.    If the return value (the Func it self) is not null then invoke it. 

     

     

    It also sounds like a good argument for adding an Algebraic datatype to C#. F# has them so it possible. 

     

    Maybe<T> {
    | Nothing           // Could equalabel to null?
    | Something<T>
    }

     

     

    mb_IEnumerable<T>{
    | Empty
    | Item<T>
    }

  • User profile image
    exoteric

    ...Of course the argument is not just for struct types, but for all types, but it's slightly more necessary for struct types...

Conversation locked

This conversation has been locked by the site admins. No new comments can be made.