35 votes

Utilisation malveillante de peut-être des méthodes de monade et d'extension en C #?

J'ai pensé à le nul de la propagation de problème .NET, ce qui conduit souvent à laide, répété code comme ceci:

Tentative n ° 1 code habituel:

string activeControlName = null;
var activeForm = Form.ActiveForm;
if (activeForm != null)
{
    var activeControl = activeForm.ActiveControl;
    if(activeControl != null)
    {
        activeControlname = activeControl.Name;
    }
}

Il y a eu quelques discussions sur StackOverflow sur un Peut-être<T> monade, ou en utilisant une sorte de "si ce n'null" méthode d'extension:

Tentative n ° 2, la méthode d'extension:

// Usage:
var activeControlName = Form.ActiveForm
                          .IfNotNull(form => form.ActiveControl)
                          .IfNotNull(control => control.Name);

// Definition:
public static TReturn IfNotNull<TReturn, T>(T instance, Func<T, TReturn> getter)
    where T : class
{
    if (instance != null ) return getter(instance);
    return null;
}

Je pense que c'est mieux, cependant, il y a un peu de désordre syntaxique-ness avec la répétition de "IfNotNull" et les lambdas. Je suis en train de réfléchir à cette conception:

Tentative n ° 3, Peut-être<T> avec la méthode d'extension

// Usage:
var activeControlName = (from window in Form.ActiveForm.Maybe()
                         from control in window.ActiveControl.Maybe()
                         select control.Name).FirstOrDefault();

// Definition:
public struct Maybe<T> : IEnumerable<T>
      where T : class
{
    private readonly T instance;

    public Maybe(T instance)
    {
        this.instance = instance;
    }

    public T Value
    {
        get { return instance; }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return Enumerable.Repeat(instance, instance == null ? 0 : 1).GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

public static class MaybeExtensions
{
    public static Maybe<T> Maybe<T>(this T instance)
        where T : class
    {
        return new Maybe<T>(instance);
    }
}

Ma question est: est-ce un mal d'abus de méthodes d'extension? Est-il mieux que l'ancienne habitude null chèques?

18voto

Daniel Earwicker Points 63298

Il est intéressant de voir que tant de personnes, indépendamment de choisir le nom de l' IfNotNull, pour ce en C# - elle doit être la plus judicieuse nom possible! :)

Plus tôt celui que j'ai trouvé sur DONC: http://stackoverflow.com/questions/123088/possible-pitfalls-of-using-this-extension-method-based-shorthand

Mon seul (dans l'ignorance de ce qui précède): http://stackoverflow.com/questions/336775/pipe-forwards-in-c/337846#337846

Un autre exemple plus récent: http://stackoverflow.com/questions/854591/how-to-check-for-nulls-in-a-deep-lambda-expression/854619#854619

Il ya un couple de raisons pour lesquelles l' IfNotNull méthode d'extension peuvent être impopulaires.

  1. Certaines personnes sont catégoriques, une méthode d'extension doit lever une exception si son this paramètre null. Je suis en désaccord si le nom de la méthode, il est clair.

  2. Les Extensions qui s'appliquent de façon trop large, aura tendance à encombrer la liste d'autocomplétion. Ceci peut être évité par une bonne utilisation des espaces de noms afin de ne pas embêter les gens qui ne veulent pas d'eux, cependant.

J'ai joué un peu avec l' IEnumerable approche aussi, juste comme une expérience pour voir comment beaucoup de choses que je pourrais torsion pour s'adapter à la les mots clés Linq, mais je pense que le résultat final est moins lisible que soit l' IfNotNull chaînage ou cru, le code impératif.

J'ai fini avec un simple auto-contenue Maybe classe avec une méthode statique (pas une méthode d'extension) et qui fonctionne très bien pour moi. Mais ensuite, je travaille avec une petite équipe, et mon prochain plus haut collègue est intéressé à la programmation fonctionnelle, et les lambdas et ainsi de suite, de sorte qu'il n'est pas rebuté par elle.

14voto

Jon Skeet Points 692016

Comme je suis un fan de méthodes d'extension, je ne pense pas que ce soit vraiment utile. Vous avez encore la répétition de l'expression (dans le monadique version), et il signifie simplement que vous avez à expliquer Maybe pour tout le monde. L'ajout de la courbe d'apprentissage ne semble pas avoir assez d'avantage dans ce cas.

L' IfNotNull version au moins parvient à éviter la répétition, mais je pense que c'est encore un peu trop longwinded, sans être plus clair.

Peut-être qu'un jour, nous allons obtenir un null-sûr référence à l'opérateur...


Juste en aparté, mon préféré semi-mal de la méthode d'extension est:

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

Qui vous permet de transformer ceci:

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

dans:

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

Il y a toujours le méchant de la répétition du nom du paramètre, mais au moins c'est plus propre. Bien sûr, dans .NET 4.0, je serais d'utiliser des Contrats de Code, qui est ce que je suis censé être écrit en ce moment... Stack Overflow est un grand travail d'évitement ;)

1voto

Mark Synowiec Points 3069

Si vous souhaitez qu'une méthode d'extension réduise le nombre imbriqué, vous pouvez essayer quelque chose comme ceci:

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

alors dans votre code, vous feriez juste:

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

Je ne sais pas si je voudrais l’utiliser souvent à cause de la lenteur de la réflexion, et je ne pense pas tellement mieux que la solution de rechange, mais cela devrait fonctionner, que vous choisissiez ou non façon...

(Remarque: j'ai peut-être mélangé ces types) :)

0voto

HeathenWorld Points 67

L’échantillon initial fonctionne et est le plus facile à lire en un coup d’œil. Est-il vraiment nécessaire d'améliorer cela?

0voto

jeroenh Points 12777

La solution IfNotNull est la meilleure (jusqu’à ce que l’équipe C # nous fournisse un opérateur de déréférencement null-safe).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X