17

部分クラスのMSDNドキュメントによると:

部分メソッドは暗黙的にプライベートです

だからあなたはこれを持つことができます

// Definition in file1.cs
partial void Method1();

// Implementation in file2.cs
partial void Method1()
{
  // method body
}

しかし、あなたはこれを持つことはできません

// Definition in file1.cs
public partial void Method1();

// Implementation in file2.cs
public partial void Method1()
{
  // method body
}

しかし、これはなぜですか?コンパイラがパブリック部分メソッドを処理できない理由はありますか?

4

9 に答える 9

27

部分的なメソッドは、コンパイル時に完全に解決可能である必要があります。それらがコンパイル時に存在しない場合、それらは出力から完全に欠落しています。部分メソッドが機能する全体的な理由は、それらを削除しても、1行の呼び出しサイトの外部のAPIまたはプログラムフローに影響がないためです(これも、voidを返さなければならない理由です)。

パブリックAPIにメソッドを追加すると、他のオブジェクトのコントラクトを定義することになります。部分的な方法の全体的なポイントはそれをオプションにすることなので、基本的には「私はあなたが信頼できる契約を結んでいます。まあ、あなたはこの方法を信頼することはできません」と言うでしょう。

パブリックAPIが合理的であるためには、部分メソッドは実際には常に存在するか、常に存在しない必要があります。この場合、部分メソッドであってはなりません。

理論的には、言語設計者は、これを可能にするために、部分メソッドの動作方法を変更できたはずです。呼び出されたすべての場所からそれらを削除する代わりに、スタブ(つまり、何もしないメソッド)を使用してそれらを実装することもできます。ただし、これはそれほど効率的ではなく、部分的な方法で想定されるユースケースには不要です。

于 2010-01-08T01:47:47.687 に答える
8

なぜ彼らがプライベートなのかを尋ねる代わりに、質問をこれに言い換えましょう:

  • 部分的なメソッドがプライベートでない場合はどうなりますか?

この簡単な例を考えてみましょう。

public partial class Calculator
{
    public int Divide(int dividend, int divisor)
    {
        try
        {
            return dividend / divisor;
        }
        catch (DivideByZeroException ex)
        {
            HandleException(ex);
            return 0;
        }
    }

    partial void HandleException(ArithmeticException ex);
}

なぜこれを抽象的な方法ではなく部分的な方法にするのかという質問は今のところ無視しましょう(私はそれに戻ります)。HandleException重要なのは、メソッドが実装されているかどうかに関係なく、これがコンパイルされ、正しく機能することです。誰もそれを実装していない場合、これは例外を食べて0を返します。

次に、ルールを変更して、部分的なメソッドを保護できるとしましょう。

public partial class Calculator
{
    // Snip other methods

    // Invalid code
    partial protected virtual void HandleException(ArithmeticException ex);
}

public class LoggingCalculator : Calculator
{
    protected override virtual void HandleException(ArithmeticException ex)
    {
        LogException(ex);
        base.HandleException(ex);
    }

    private void LogException(ArithmeticException ex) { ... }
}

ここで少し問題があります。HandleExceptionまだオーバーライドするメソッドがないことを除いて、メソッドを「オーバーライド」しました。そして、私はそのメソッドが文字通り存在しないことを意味します、それはまったくコンパイルされていません。

Calculator私たちの基地が呼び出すものはどういう意味HandleExceptionですか?派生(オーバーライド)メソッドを呼び出す必要がありますか?もしそうなら、コンパイラは基本HandleExceptionメソッドに対してどのコードを出力しますか?それを抽象的なメソッドに変えるべきですか?空のメソッド?そして、派生メソッドが呼び出すとどうなりますbase.HandleExceptionか?これは何もしないはずですか?MethodNotFoundException?_ ここで驚き最小の原則に従うのは本当に難しいです。あなたがするほとんどすべては驚くべきことになるでしょう。

HandleExceptionまたは、基本メソッドが実装されていないため、が呼び出されても何も起こらないはずです。ただし、これはあまり直感的ではないようです。私たちの派生クラスはこのメソッドを実行して実装し、基本クラスは私たちが知らないうちにその下からラグを引き出しました。貧しい開発者が髪の毛を抜いて、オーバーライドされたメソッドが実行されない理由を理解できないことは容易に想像できます。

または、このコードはまったくコンパイルされないか、警告が表示されるはずです。しかし、これには独自の問題がいくつかあります。最も重要なことは、部分的なメソッドによって提供されるコントラクトを破ることです。つまり、部分的なメソッドの実装を怠ると、コンパイラーエラーが発生することはありません。基本クラスがうまく機能しているのに、誰かがアプリケーションのまったく異なる部分に完全に有効な派生クラスを実装したために、突然アプリが壊れてしまいます。

また、基本クラスと派生クラスが異なるアセンブリにある可能性についても話し始めていません。「パブリック」部分メソッドを含む基本クラスでアセンブリを参照し、それを別のアセンブリの派生クラスでオーバーライドしようとするとどうなりますか?基本メソッドはありますか、ありませんか?元々、メソッドが実装されていて、それに対して一連のコードを記述したが、誰かが実装を削除することにした場合はどうなりますか?コンパイラーに関する限り、そのメソッドはそもそも存在しなかったため、コンパイラーには、参照クラスからの部分的なメソッド呼び出しをスタブ化する方法がありません。。そこにはありません。コンパイルされたアセンブリのILにはもうありません。だから今、悪影響がないと思われる部分的なメソッドの実装を削除するだけで、依存するコードの束全体が壊れてしまいました。

今、一部の人々は、「だから、私はこの違法なことを部分的な方法でやろうとはしないことを知っている」と言っているかもしれません。理解しなければならないのは、部分メソッド(部分クラスによく似ています)は、主にコード生成のタスクを単純化することを目的としているということです。自分で部分的なメソッドを書きたいと思うことはほとんどありません。一方、マシンで生成されたコードでは、コードの利用者がさまざまな場所にコードを「注入」したいと思う可能性が実際に高く、部分的なメソッドはこれを行うためのクリーンな方法を提供します。

そしてそこに問題があります。部分的なメソッドが原因でコンパイル時エラーが発生する可能性がある場合は、コードジェネレーターがコンパイルされないコードを生成する状況が発生します。これは非常に悪い状況です。お気に入りのデザイナーツール(Linq to SQL、WinformsまたはASP.NETデザイナーなど)が突然コンパイルに失敗するコードの作成を開始した場合の対処方法を考えてみてください。他のプログラマーが、これまでに見たことのない他のクラスを作成したため、部分メソッドに少し親密になりすぎたのでしょうか。

結局のところ、それは実際にははるかに単純な質問に要約されます。パブリック/保護された部分メソッドは、抽象メソッドではまだ達成できないものを追加しますか?パーシャルの背後にある考え方は、それらを具象クラスに置くことができ、それでもコンパイルできるということです。むしろ、それらコンパイルされませんが、エラーも生成せず、完全に無視されます。しかし、それらが公に呼び出されることを期待する場合、それらはもはや実際には「オプション」ではなく、派生クラスでオーバーライドされることを期待する場合は、抽象的または仮想的で空にすることもできます。コンパイラと貧弱な野郎がすべてを理解しようとしていることを混乱させる以外に、パブリックまたは保護された部分メソッドの使用は実際にはありません。

したがって、チームは、そのPandoraのボックスを開く代わりに、それを忘れると言いました。パブリック/保護された部分メソッドは、とにかくあまり使用されないので、プライベートにするだけです。そうすれば、すべてを安全で正気に保つことができます。そしてそうです。部分的なメソッドが非公開である限り、それらは理解しやすく、心配がありません。そのままにしておきましょう!

于 2010-03-19T03:05:16.927 に答える
6

MSコンパイラチームには、この機能を実装するための要件が​​なかったためです。

VSはその機能の多くにコード生成を使用するため、MS内で発生する可能性のあるシナリオを次に示します。ある日、MSコード生成開発者は、コード生成APIを拡張できるように部分的なメソッドが必要であると判断しました。部外者であり、この要件により、コンパイラチームは行動して提供することになりました。

于 2010-03-24T03:24:09.277 に答える
3

メソッドを定義しない場合、コンパイラは何をすべきですか?

それらを定義しないと、部分的なメソッドはまったくコンパイルさません。
これが可能なのは、コンパイラがメソッドへのすべての呼び出しがどこにあるかを知っているからです。メソッドが定義されていない場合は、メソッドを呼び出すコード行が完全に削除されます。(これは、彼らが戻ることしかできない理由でもありますvoid

メソッドがパブリックの場合、これは機能しません。これは、メソッドが別のアセンブリによって呼び出される可能性があり、コンパイラが制御できないためです。

于 2010-01-08T01:02:00.493 に答える
2

ReedとSlakの答えは完全に正しいですが、あなたはそれらの答えを受け入れたくないようです。

したがって、これらの制限付きで部分メソッドが実装される理由を説明しようと思います。

部分的なメソッドは、実行時間とメタデータのオーバーヘッドの両方で最大の効率で特定の種類のコード生成シナリオを実装するためのものです。最後の部分は、彼らが(エリックスの言葉で)彼らを「遊びのために支払う」ようにしようとしているので、彼らの本当の理由です。

部分的なメソッドが追加されたとき、JITは空のメソッドを完全にインライン化できるため、呼び出しサイトでの実行時の労力はゼロでした。問題は、それでも、クラスのメタデータにこれらの空のメソッドが含まれ、サイズが(不必要に)大きくなるだけでなく、JITingプロセス中にそれらの最適化に対処するためにさらに労力がかかるというコストがかかることです。

このコストについてはあまり心配しないかもしれませんが(実際、多くの人はまったく気付かないでしょう)、起動コストが重要な場合やディスク/メモリが制約されている場合のコードには大きな違いがあります。Windows Mobile 7とZuneで.Netを使用することが義務付けられていることに気付いたかもしれませんが、これらの領域では、タイプメタデータの肥大化にはかなりのコストがかかります。

したがって、部分的なメソッドは、使用されない場合でもコストがまったくかからないように設計されており、出力に存在しなくなります。これには、バグが発生しないようにするためのいくつかの重要な制約があります。

私のメモと一緒にmsdnページから取得。

  • ...メソッドはvoidを返す必要があります。
  • 部分メソッドはrefを持つことができますが、outパラメーターを持つことはできません。

そうしないと、それらへの呼び出しを削除すると、割り当てを何に置き換えるかという未定義の問題が残る可能性があります。

  • 本体の存在によって、メソッドが定義されているか実装されているかが決まるため、部分メソッドを外部化することはできません。
  • 定義および実装された部分メソッドへのデリゲートを作成できますが、定義されただけの部分メソッドへのデリゲートは作成できません。

これらは、コンパイラが定義されているかどうかを知る必要があるという事実に基づいているため、削除しても安全です。これはあなたが嫌い​​なものに私たちを導きます。

  • 部分メソッドは暗黙的にプライベートであるため、仮想にすることはできません。

この機能の金属モデルは、コンパイラが部分メソッドが実装されていることを知っている場合、メソッドを実装したことを確認できるため、部分メソッドをパブリック(さらに言えば仮想)にするだけでよいということです。

これを行うために機能を変更した場合、2つのオプションがあります。

  1. すべての非プライベート部分メソッドに実装を要求するように強制します。

    • 単純で、多くの労力を必要としない可能性がありますが、そのような方法は、元の計画の意味のある意味でもはや部分的ではありません。
  2. publicと宣言されたメソッドは、アセンブリに実装されていない場合は単に削除されます。

    • これにより、部分的な削除を適用できます
    • コンパイラーによるかなり多くの労力が必要です(単に構成されたクラス自体を調べる必要があるのではなく、メソッドへのすべての参照を検出するため)
    • IntelliSenseの実装は少し混乱していますが、メソッドを表示する必要がありますか?定義が与えられたときにのみ播種されますか?
    • 定義のないこのようなメソッドの呼び出しがa)コンパイル時の失敗であるか、b)可能な場合は次善のオプションが選択されるかを判断する必要があるため、過負荷の解決ははるかに複雑になります。
    • 呼び出しサイトでの式内の副作用は、プライベートのみの場合ではすでに複雑です。これは、部分的な実装がすでに高度な結合を示しているという仮定によっていくらか軽減されます。この変更により、結合の可能性が高まります。
    • これにはかなり複雑な障害モードがあり、元のアセンブリの無害な変更によってパブリックメソッドがサイレントに削除される可能性があります。これに依存する他のアセンブリは(コンパイル時に)失敗しますが、非常に紛らわしいエラーが発生します(かなりの労力がなければ、これは同じソリューションのプロジェクトにも適用されます)。

解決策1は単純に無意味であり、労力が追加され、元の目標には何のメリットもありません。さらに悪いことに、このように部分的なメソッドを使用している人は、メタデータが削減されていないことに気付かない可能性があるため、実際には元の目標を妨げる可能性があります。また、定義の提供に失敗した結果として生じるエラーについて誰かが混乱する可能性があります。

これで解決策2が残ります。この解決策には労力が必要です(すべての機能は-100で始まります)。したがって、-100を超えるには、説得力のある利点を追加する必要があります(追加のエッジケースではなく、混乱のために追加の欠点が追加されます)。 )。物事を前向きにするためにどのようなシナリオを思いつくことができますか?

上記のコメントでのあなたのやる気を起こさせる例は、「コメント/および/または属性を別のファイルに入れる」ことでした

XMLコメントの動機は、完全にドキュメントとコードの局所性を高めることでした。それらの冗長性が高い場合は、これを軽減するためのアウトラインが存在します。私の意見では、これは機能に値するのに十分ではありません。

属性を別の場所にプッシュする機能は、私の見解ではそれほど有用ではありません。実際、2つの場所を調べなければならない方が混乱していると思います。現在のプライベートのみの実装にもこの問題がありますが、これは避けられず、クラスの外部では見えない高結合はクラスの外部の高結合ほど悪くはないという仮定によって、ある程度軽減されます。

他の説得力のある理由を示すことができれば、それは興味深いことだと思いますが、克服すべき欠点はかなりあります。

于 2010-03-20T19:53:19.247 に答える
2

私はあなたのコメントをすべて読み、結論のほとんどに同意しますが、元の意図を失うことなく、パブリック/保護された部分的なメソッドから利益を得ると思うシナリオが1つあります。

シリアル化コードやその他の定型コードを生成するコードジェネレーターがあります。特に、VSのような設計者がその値を元に戻すことができるように、すべてのプロパティに対して「リセット」メソッドを生成します。このメソッドのコード内で、部分メソッドRepaint()の呼び出しを生成します。

オブジェクトが必要な場合は、そのコードを記述してから何かを実行できます。それ以外の場合は、何も実行されず、パフォーマンスが最適になります。

問題は、生成されたコードから呼び出される以外の目的でRepaintメソッドがオブジェクトに存在する場合があります。その時点で、メソッド本体を宣言すると、内部、保護、またはパブリックにすることができるはずです。この時点でメソッドを定義しています。はい、ここで、生成した宣言ではなく、手作業で作成した宣言などを文書化します。生成されたコードでも定義されているという事実は、これに影響を与えるべきではありません。

本文なしでメソッドを宣言する場合、部分メソッドでアクセス修飾子を定義しても意味がないことに同意します。本体を使用してメソッドを宣言する場合は、自由に宣言できます。コンパイラがこのシナリオを受け入れるための複雑さの点で違いは見られません。

部分クラスでは、これはまったく問題ありません。アクセス修飾子を使用せずに部分宣言の1つを「裸」のままにすることはできますが、同じクラスの別の部分宣言で指定することはできます。矛盾がない限り、すべてが大丈夫です。

于 2011-03-30T19:10:08.523 に答える
1

partial実装がない場合、メソッドはコンパイラによって削除されます。partial私の理解によると、メソッドが次のようにアクセス可能である場合、コンパイラはメソッドを削除できません。public

//partial class with public modifier
public partial class Sample 
{
    partial void Display();
}

public partial class Sample
{
    partial void Display() { }

}

以下のようなメソッドにアクセスできるようになります。これは、実装されていないメソッドpublicをコンパイラーが削除するための制限になります。partial

// referred partial method which doesn't have implementation 
var sample = new Sample();
sample.Display();
于 2017-05-11T22:27:19.953 に答える
0

これがあなたの質問に答えるかどうかわからない、それは私の答えでした。 部分メソッド

抜粋:

何かを返すことが許可されていて、それをCallMyMethods関数からの戻り値として使用している場合、部分的なメソッドが実装されていないと問題が発生します。

于 2010-01-08T01:00:36.050 に答える
0

単純な理由は、部分的なメソッドは実装の詳細であるため、公開されていないことです。これらは主にデザイナー関連のシナリオをサポートすることを目的としており、サポートされているパブリックAPIの一部となることを意図したものではありません。ここでは、非公開の方法で問題なく機能します。

部分的なメソッドを公開できるようにするのは機能です。機能には、設計、テスト、開発などの固有のコストがあります...部分的なメソッドは、非常にパックされたVisualStudio2008に追加された多くの機能の1つにすぎません。 LINQなどのより差し迫った機能のための余地。

于 2010-03-23T22:24:51.033 に答える