質問

2015年を編集 この質問とその回答はもう意味がありません。これは、この質問とその後の回答で説明されているハッキングな回避策を回避する null 伝播演算子 (?.) を備えた C# 6 が登場する前に質問されました。2015 年の時点では、C# では Form.ActiveForm?.ActiveControl?.Name を使用する必要があります。


.NET の null 伝播の問題について考えてきました。この問題により、次のような醜い繰り返しコードが生成されることがよくあります。

#1 の通常のコードを試します。

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

StackOverflow では、Maybe<T> モナド、またはある種の「if not null」拡張メソッドの使用についていくつかの議論がありました。

試み #2、拡張メソッド:

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

これはより良いと思いますが、「IfNotNull」の繰り返しとラムダの構文が少し乱雑です。現在、こんなデザインを検討中です。

試み #3、拡張メソッドを使用したMaybe<T>

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

私の質問は:これは拡張メソッドの悪用でしょうか?従来の通常の null チェックよりも優れているのでしょうか?

役に立ちましたか?

解決

非常に多くの人が独自に名前を選んだのは興味深いことです IfNotNull, 、これを C# で行う場合は、可能な限り最も賢明な名前にする必要があります。:)

SOで見つけた最も古いもの: この (拡張メソッドベースの) 短縮表現を使用する場合に考えられる落とし穴

私のもの(上記のことを知らずに): C# でのパイプフォワード

もう一つのより最近の例: 深いラムダ式で null をチェックするにはどうすればよいですか?

理由はいくつかありますが、 IfNotNull 拡張メソッドは人気がないかもしれません。

  1. 一部の人々は、次の場合には拡張メソッドが例外をスローするべきだと主張しています。 this パラメータは null. 。メソッド名がそれを明確にしているかどうかは、私は同意しません。

  2. 適用範囲が広すぎる拡張機能は、オートコンプリート メニューを乱雑にする傾向があります。ただし、これは名前空間を適切に使用することで回避でき、名前空間を必要としない人に迷惑をかけないようにすることができます。

で遊んでみました IEnumerable このアプローチは、Linq キーワードに適合するためにどれだけ多くのことをひねることができるかを確認するための単なる実験でもありますが、最終結果はどちらの場合よりも読みにくくなると思います。 IfNotNull チェーンまたは生の命令型コード。

シンプルな自己完結型になりました Maybe 1 つの静的メソッド (拡張メソッドではない) を持つクラスであり、それは私にとって非常にうまく機能します。しかし、私は少人数のチームで働いており、次に先輩の同僚は関数型プログラミングやラムダなどに興味を持っているので、それを嫌がることはありません。

他のヒント

私は拡張メソッドのファンだ限り、私はこれは本当に便利だとは思いません。あなたはまだ(モナドバージョンで)表現の繰り返しを持っている、そしてそれはちょうどあなたが皆にMaybeを説明するために持っていることを意味します。追加学習曲線は、この場合には十分な利益を持っていないようです。

IfNotNullのバージョンは、少なくとも繰り返しを避けるために管理し、私はそれが実際に明確にされることなく、まだ少しだけあまりにもlongwindedだと思います。

たぶん1日、我々はヌル・安全な間接参照演算子を取得します...

<時間>

はさておき、私のお気に入りの半邪悪な拡張メソッドがあるのと同様ます:

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

それはあなたがこれを回すことができます:

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

に:

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

まだそこにパラメータ名の厄介な繰り返しですが、少なくともそれは整然とです。もちろん、.NET 4.0で、私は私が今について書くことを意味してる何である、コードコントラクトを使用したい...スタックオーバーフローは素晴らしい仕事を回避することである;)

あなたが持っているようなものだが、あなたがこのような何かをしようとする可能性がある場合、拡張メソッドは、ネストされたを削減したい場合:

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

ので、あなたのコード内であなただけやると思います:

string activeControlName = (Form.ActiveForm as object)
    .GetProperty(typeof(Form),"ActiveControl")
    .GetProperty(typeof(Control),"Name");
私はしばしばによる反射の遅さにそれを使用したいと思う場合は、

私は知らない、と私は本当に選択肢よりも、このはるかに良いとは思わないが、それは関係なく、あなたがヒットするかどうかの、動作するはずです道に沿ってヌル...

(注:私は混ぜこれらの型を得ているかもしれません):)

場合はあなたが上記のC#6.0 / VS 2015とを扱っている、彼らは今、ヌル伝播ための組み込みソリューションを持っています:

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

最初のサンプルは機能しており、一目で読むのが最も簡単です。本当にそれを改善する必要があるのでしょうか?

IfNotNull溶液(C#のチームは私たちにヌル・安全な間接参照演算子を与えるまで、つまり)が最適です。

私はどちらかのソリューションについて、あまりにも狂っていませんよ。

:オリジナルのashorter版の何が問題でした
string activeControlName = null;
if (Form.ActiveForm != null)
    if (Form.ActiveForm.ActivControl != null) activeControlname = activeControl.Name;

ではない、この場合、私は缶チェーン行の数nullでないテストよりNotNullChainまたはFluentNotNullオブジェクトを書いて見ます。私はヌルに作用するIfNotNull拡張メソッドは少し奇妙なようだということに同意 - 拡張メソッドは、単に構文糖であっても、

私はマークSynowiecの答えは、ジェネリック作っすることができるかもしれないと考えています。

IMHO、私は私が取り組むために大きなものがあると思いますが、C#のコアチームは、この「問題」を見てすべきだと思います。

確かに、オリジナルの2-ネストされたIFは、はるかに読みやすい他の選択肢よりもあります。しかし、あなたはここで、より一般的な問題を解決したい示唆して別の解決策はあります:

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();
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top