web-dev-qa-db-ja.com

プライベート拡張メソッドはありますか?

簡単なプライベートヘルパーメソッドが必要だとしましょう。直感的には、コードでは拡張メソッドとして意味があります。そのヘルパーを実際に使用する必要がある唯一のクラスにカプセル化する方法はありますか?

たとえば、私はこれを試します:

_class Program
{
    static void Main(string[] args)
    {
        var value = 0;
        value = value.GetNext(); // Compiler error
    }

    static int GetNext(this int i)
    {
        return i + 1;
    }
}
_

コンパイラーは、GetNext()拡張メソッドを「認識」しません。エラーは:

拡張メソッドは非ジェネリック静的クラスで定義する必要があります

十分に公正なので、それを独自のクラスでラップしますが、それが属しているオブジェクト内にカプセル化されています。

_class Program
{
    static void Main(string[] args)
    {
        var value = 0;
        value = value.GetNext(); // Compiler error
    }

    static class Extensions
    {
        static int GetNext(this int i)
        {
            return i + 1;
        }
    }
}
_

まだサイコロはありません。エラーは次のようになります:

拡張メソッドは、トップレベルの静的クラスで定義する必要があります。拡張機能はネストされたクラスです。

この要件に説得力のある理由はありますか?ヘルパーメソッドを実際に非公開でカプセル化する必要がある場合と、ヘルパーメソッドが拡張メソッドの場合、コードがより明確で読みやすく/サポートしやすい場合があります。これら2つが交差する場合、両方を満たすことができますか、それともどちらか一方を選択する必要がありますか?

46
David

私はあなたが一般的に得ることができる最高のものはinternal staticクラスとinternal static拡張メソッドを持っていると信じています。それはあなた自身のアセンブリになるので、拡張機能の使用を防ぐ必要があるのはアセンブリの作成者だけです-したがって、明示的に名前が付けられた名前空間(My.Extensions.ForFoobarOnlyなど)は、誤用を避けるためのヒントとして十分な場合があります。

実装拡張 記事でカバーされている最小限のinternal制限

クラスは、少なくとも包含クラスと同じ可視性を持つクライアントコード...メソッドから可視でなければなりません。

注:ユニットテストを簡略化するためにとにかく拡張機能を公開しますが、Xxxx.Yyyy.Internalなどの明示的に名前が付けられた名前空間に入れて、アセンブリの他のユーザーがメソッドのサポート/呼び出しを期待しないようにします。基本的に、コンパイル時の強制以外の規則に依存します。

24
Alexei Levenkov

この要件に説得力のある理由はありますか?

これは間違った質問です。この機能を設計しているときに言語設計チームが尋ねた質問は次のとおりです。

ネストされた静的型で拡張メソッドを宣言できるようにする説得力のある理由はありますか?

拡張メソッドはLINQを機能させるように設計されており、LINQには拡張メソッドが型に対してプライベートになるシナリオがないため、答えは「いいえ、そのような説得力のある理由はありません」でした。

静的なネストされた型に拡張メソッドを配置する機能を排除することで、静的なネストされた型の拡張メソッドを検索するためのルールを検討したり、議論したり、設計、指定、実装、テスト、文書化、顧客に出荷したり、 C#の今後のすべての機能と互換性があります。これは大幅なコスト削減でした。

46
Eric Lippert

このコードはコンパイルして機能します。

static class Program
{
    static void Main(string[] args)
    {
        var value = 0;
        value = value.GetNext(); // Compiler error
    }

    static int GetNext(this int i)
    {
        return i + 1;
    }
}

注意を払う static class Programコンパイラが言った行が必要です。

1

彼らが拡張メソッドのコンパイルを実装した方法だと思います。

ILを見ると、メソッドにいくつかの追加属性が追加されているようです。

.method public hidebysig static int32 GetNext(int32 i) cil managed
{
    .custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor()
    .maxstack 2
    .locals init (
        [0] int32 num)
    L_0000: nop 
    L_0001: ldarg.0 
    L_0002: ldc.i4.1 
    L_0003: add 
    L_0004: dup 
    L_0005: starg.s i
    L_0007: stloc.0 
    L_0008: br.s L_000a
    L_000a: ldloc.0 
    L_000b: ret 
}

おそらく私たちが欠けている非常に根本的なものはおそらく機能しないので、制限が設けられています。また、コーディング手法を強制したかっただけかもしれません。残念ながら、それは機能せず、トップレベルの静的クラスにある必要があります。

1
TyCobb