Question

modifier 2015 Cette question et ses réponses ne sont plus pertinentes. Il a été demandé avant l'avènement de C # 6, qui a la opertor de propagation nulle (?.), Qui évite les aki-solutions de contournement décrites dans cette question et les réponses suivantes. En 2015, en C # vous devez maintenant utiliser Form.ActiveForm? .ActiveControl? .Nom.


J'ai réfléchi sur le problème de propagation nulle dans .NET, ce qui conduit souvent à laid, le code répété comme ceci:

Tentative n ° 1 du code d'habitude:

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 Maybe monade, ou en utilisant une sorte de « sinon null » méthode d'extension:

Tentative n ° 2, 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 cela vaut mieux, cependant, il y a un peu de désordre-ness syntaxique avec la répétition « IfNotNull » et le lambdas. Je considère maintenant cette conception:

Tentative n ° 3, peut-être 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 abus mal des méthodes d'extension? Est-il préférable que les anciens contrôles habituels nuls?

Était-ce utile?

La solution

Il est intéressant de noter que beaucoup de gens choisissent indépendamment le nom IfNotNull, pour cela en C # - il doit être le nom le plus raisonnable possible! :)

au plus tôt que je l'ai trouvé sur le SO: possibles pièges l'utilisation de ce raccourci (méthode d'extension sur la base)

Mon seul (dans l'ignorance de ce qui précède): avant de tuyaux en C #

Un autre exemple plus récent: Comment vérifier dans une expression nulls lambda profonde?

Il y a deux raisons pour lesquelles la méthode d'extension de IfNotNull peut être impopulaire.

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

  2. Les extensions qui appliquent trop largement tendront à encombrer le menu auto-complétion. Cela peut être évité par l'utilisation appropriée des espaces de noms afin qu'ils ne dérangent pas les gens qui ne veulent pas, cependant.

Je l'ai joué avec l'approche IEnumerable aussi, tout comme une expérience pour voir combien de choses que je pourrais tourner pour adapter les mots-clés Linq, mais je pense que le résultat final est moins lisible que soit l'enchaînement de IfNotNull ou l'impératif brut code.

J'ai fini avec une simple classe Maybe autonome avec une méthode statique (pas une méthode d'extension) et qui fonctionne très bien pour moi. Mais alors, je travaille avec une petite équipe, et mon prochain collègue le plus ancien est intéressé par la programmation fonctionnelle et lambdas et ainsi mettre, donc il est pas rebutés par lui.

Autres conseils

Bien que 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 des expressions (dans la version monadique), et cela signifie simplement que vous avez à expliquer Maybe à tout le monde. La courbe d'apprentissage ajouté ne semble pas avoir assez avantage dans ce cas.

La version IfNotNull au moins parvient à éviter la répétition, mais je pense qu'il est encore un peu trop verbeux sans réellement être plus clair.

Peut-être un jour, nous aurons un opérateur déréférencement null-safe ...


En passant, ma méthode préférée d'extension semi-mal est:

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

Cela vous permet de tourner ceci:

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

dans:

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

Il y a encore la répétition méchant du nom de paramètre, mais au moins il est plus net. Bien sûr, dans .NET 4.0 J'utilise des contrats de code, qui est ce que je voulais écrire sur ce moment ... Stack Overflow est grande évasion de travail;)

Si vous voulez une méthode d'extension pour réduire la imbriquée si ce comme vous avez, 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;
}

dans votre code que vous souhaitez simplement faire:

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

Je ne sais pas si je veux l'utiliser pour souvent en raison de la lenteur de la réflexion, et je ne pense pas vraiment cela beaucoup mieux que l'alternative, mais il devrait fonctionner, peu importe si vous frappez un null le long du chemin ...

(Note: Je pourrait avoir obtenu ces types mélangés):)

Si vous avez affaire à C # 6.0 / VS 2015 et au-dessus, ils ont maintenant une solution intégrée pour la propagation nulle:

string ans = nullableString?.Length.ToString(); // null if nullableString == null, otherwise the number of characters as a string.

Les premiers travaux d'échantillonnage et est le plus facile à lire en un coup d'oeil. Est-il vraiment nécessaire d'améliorer à ce sujet?

La solution IfNotNull est le meilleur (jusqu'à ce que l'équipe C # nous donne un opérateur de déréférencement nul en toute sécurité, c'est).

Je ne suis pas trop fou de solution soit. Quel était le problème avec la version ashorter de l'original:

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

Si pas, alors je regardais écrire un NotNullChain ou objet FluentNotNull que peut chaîner quelques essais non nuls d'affilée. Je suis d'accord que la méthode d'extension IfNotNull agissant sur un nul semble un peu bizarre -. Même si les méthodes d'extension ne sont que du sucre syntaxique

Je pense que la réponse de Mark Synowiec pourrait être en mesure de fait générique.

à mon humble avis, je pense que l'équipe C # centrale devrait examiner la cette « question », même si je pense qu'il ya des choses plus importantes à aborder.

Bien sûr, 2-IF imbriquées est beaucoup plus lisible originale que d'autres choix. Mais ce qui suggère que vous voulez résoudre le problème de façon plus générale, voici une autre solution:

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

class AssumptionChainFailed : Exception { }
void assumeIsNotNull(object obj)
{
    if (obj == null) throw new AssumptionChainFailed();
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top