Evil use of Maybe monad and extension methods in C#?

前端 未结 8 1841
面向向阳花
面向向阳花 2020-12-07 17:11

edit 2015 This question and its answers are no longer relevant. It was asked before the advent of C# 6, which has the null propagating opertor (?.)

相关标签:
8条回答
  • 2020-12-07 17:16

    It's interesting that so many people independently pick the name IfNotNull, for this in C# - it must be the most sensible name possible! :)

    Earliest one I've found on SO: Possible pitfalls of using this (extension method based) shorthand

    My one (in ignorance of the above): Pipe forwards in C#

    Another more recent example: How to check for nulls in a deep lambda expression?

    There are a couple of reasons why the IfNotNull extension method may be unpopular.

    1. Some people are adamant that an extension method should throw an exception if its this parameter is null. I disagree if the method name makes it clear.

    2. Extensions that apply too broadly will tend to clutter up the auto-completion menu. This can be avoided by proper use of namespaces so they don't annoy people who don't want them, however.

    I've played around with the IEnumerable approach also, just as an experiment to see how many things I could twist to fit the Linq keywords, but I think the end result is less readable than either the IfNotNull chaining or the raw imperative code.

    I've ended up with a simple self-contained Maybe class with one static method (not an extension method) and that works very nicely for me. But then, I work with a small team, and my next most senior colleague is interested in functional programming and lambdas and so on, so he isn't put off by it.

    0 讨论(0)
  • 2020-12-07 17:17

    If you want an extension method to reduce the nested if's like you have, you might try something like this:

    public static object GetProperty(this object o, Type t, string p)
    {
        if (o != null)
        {
            PropertyInfo pi = t.GetProperty(p);
            if (pi != null)
            {
                return pi.GetValue(o, null);
            }
            return null;
        }
        return null;
    }
    

    so in your code you'd just do:

    string activeControlName = (Form.ActiveForm as object)
        .GetProperty(typeof(Form),"ActiveControl")
        .GetProperty(typeof(Control),"Name");
    

    I don't know if I'd want to use it to often due to the slowness of reflection, and I don't really think this much better than the alternative, but it should work, regardless of whether you hit a null along the way...

    (Note: I might've gotten those types mixed up) :)

    0 讨论(0)
  • 2020-12-07 17:20

    Much as I'm a fan of extension methods, I don't think this is really helpful. You've still got the repetition of the expressions (in the monadic version), and it just means that you've got to explain Maybe to everyone. The added learning curve doesn't seem to have enough benefit in this case.

    The IfNotNull version at least manages to avoid the repetition, but I think it's still just a bit too longwinded without actually being clearer.

    Maybe one day we'll get a null-safe dereferencing operator...


    Just as an aside, my favourite semi-evil extension method is:

    public static void ThrowIfNull<T>(this T value, string name) where T : class
    {
        if (value == null)
        {
            throw new ArgumentNullException(name);
        }
    }
    

    That lets you turn this:

    void Foo(string x, string y)
    {
        if (x == null)
        {
            throw new ArgumentNullException(nameof(x));
        }
        if (y == null)
        {
            throw new ArgumentNullException(nameof(y));
        }
        ...
    }
    

    into:

    void Foo(string x, string y)
    {
        x.ThrowIfNull(nameof(x));
        y.ThrowIfNull(nameof(y));
        ...
    }
    

    There's still the nasty repetition of the parameter name, but at least it's tidier. Of course, in .NET 4.0 I'd use Code Contracts, which is what I'm meant to be writing about right now... Stack Overflow is great work avoidance ;)

    0 讨论(0)
  • 2020-12-07 17:20

    I'm not too crazy about either solution. What was wrong with ashorter version of the original:

    string activeControlName = null;
    if (Form.ActiveForm != null)
        if (Form.ActiveForm.ActivControl != null) activeControlname = activeControl.Name;
    

    If not this, then I would look at writing a NotNullChain or FluentNotNull object than can chain a few not null tests in a row. I agree that the IfNotNull extension method acting on a null seems a little weird - even though extension methods are just syntactic sugar.

    I think Mark Synowiec's answer might be able to made generic.

    IMHO, I think the C# core team should look at the this "issue", although I think there are bigger things to tackle.

    0 讨论(0)
  • 2020-12-07 17:25

    The initial sample works and is the easiest to read at a glance. Is there really a need to improve on that?

    0 讨论(0)
  • 2020-12-07 17:26

    Sure, original 2-nested IF is much more readable than other choices. But suggesting you want to solve problem more generally, here is another solution:

    try
    {
        var activeForm = Form.ActiveForm; assumeIsNotNull(activeForm);
        var activeControl = activeForm.ActiveControl; assumeIsNotNull(activeControl);
        var activeControlname = activeControl.Name;
    }
    catch (AssumptionChainFailed)
    {
    }
    

    where

    class AssumptionChainFailed : Exception { }
    void assumeIsNotNull(object obj)
    {
        if (obj == null) throw new AssumptionChainFailed();
    }
    
    0 讨论(0)
提交回复
热议问题