web-dev-qa-db-ja.com

C#がC ++のような非メンバー関数を許可しない理由

C#では、非メンバー関数を作成することはできず、すべてのメソッドはクラスの一部である必要があります。これをすべてのCLI言語の制限と考えていました。しかし、私は間違っていて、C++/CLIが非メンバー関数をサポートしていることがわかりました。コンパイルされると、コンパイラはメソッドを名前のないクラスのメンバーとして作成します。

これがC++/CLI標準が言うことです、

[注:非メンバー関数は、CLIによって名前のないクラスのメンバーとして扱われます。ただし、C++/CLIソースコードでは、そのような関数をそのクラス名で明示的に修飾することはできません。エンドノート]

メタデータ内の非メンバー関数のエンコードは指定されていません。 [注:このような関数はパブリックに可視化できないため、相互運用の問題は発生しません。エンドノート]

だから私の質問は、なぜC#がこのようなものを実装しないのですか?それとも、非メンバー関数があってはならず、すべてのメソッドがいくつかのクラスに属すべきであると思いますか?

私の意見は、非メンバー関数のサポートを提供することであり、それはクラスのインターフェースの汚染を回避するのに役立ちます。

何かご意見は..?

65
Navaneeth K N

このブログ投稿を参照してください:

http://blogs.msdn.com/ericlippert/archive/2009/06/22/why-doesn-t-c-implement-top-level-methods.aspx

(...)

「C#が機能Xを実装しないのはなぜですか?」いつも。答えは常に同じです。誰もその機能を設計、指定、実装、テスト、文書化、出荷したことがないためです。これらの6つすべては、機能を実現するために必要です。それらのすべては、莫大な量の時間、労力とお金を要します。機能は安くはありません。限られた時間、労力、および予算でユーザーに最大のメリットをもたらす機能のみを出荷するように努めています。

私はそのような一般的な答えはおそらく特定の質問を扱っていないことを理解しています。

この特定のケースでは、ユーザーにとっての明らかなメリットは、これまでのところ、言語の複雑化を正当化するのに十分な大きさではありませんでした。異なる言語エンティティが相互にネストする方法を制限することにより、(1)法的プログラムを共通の簡単に理解できるスタイルに制限し、(2)包括的、特定可能、実装可能、テスト可能な「識別子ルックアップ」ルールを定義できるようにします。と文書化できます。

メソッドの本体を常に構造体またはクラス内にあるように制限することで、呼び出しコンテキストで使用される非修飾識別子の意味を簡単に推論できます。そのようなものは、常に現在の型(または基本型)の呼び出し可能なメンバーです。

(...)

そして、このフォローアップ投稿:

http://blogs.msdn.com/ericlippert/archive/2009/06/24/it-already-is-a-scripting-language.aspx

(...)

すべての設計上の決定と同様に、多くの競合する、説得力のある、価値のある、不可能ではないアイデアに直面したとき、実行可能な妥協点を見つける必要があります。 すべての可能性を考慮してを除いて、これは行いません。

(元のテキストからの強調)

86
Eric Lippert

Javaが許可しなかったため、C#では許可されていません。

Javaの設計者がおそらくそれを許可しなかった理由はいくつか考えられます

  • Javaはシンプルになるように設計されています。彼らはランダムなショートカットなしで言語を作ろうとしました。そのため、他のアプローチがより簡潔で簡潔であったとしても、すべてを行う簡単な方法は通常1つだけです。彼らは学習曲線を最小限に抑えたかったので、「クラスにメソッドが含まれている可能性がある」という学習は、「クラスにメソッドが含まれている可能性があり、関数がクラスの外部に存在する可能性がある」よりも簡単です。
  • 表面的には、オブジェクト指向ではないように見えます。 (オブジェクトの一部ではないものは明らかにオブジェクト指向にすることはできませんか?もちろん可能ですか?もちろん、C++はそう言っていますが、C++はこの決定に関与していませんでした)

すでにコメントで述べたように、これは良い質問だと思います。メンバー以外の関数が望ましい場合もたくさんあります。 (この部分は主に、「それは必要ない」と言う他のすべての回答への応答です)

非メンバー関数が許可されているC++では、いくつかの理由により、それらがしばしば推奨されます。

  • カプセル化を支援します。クラスのプライベートメンバーにアクセスできるメソッドが少ないほど、そのクラスのリファクタリングやメンテナンスが容易になります。カプセル化はOOPの重要な部分です。
  • コードがクラスの一部ではない場合、コードをより簡単に再利用できます。たとえば、C++標準ライブラリはstd::findまたはstd :: sort`を非メンバー関数として使用すると、配列、セット、リンクリスト、または(少なくともstd :: findの場合は)ストリームなど、あらゆるタイプのシーケンスで再利用できます。コードの再利用もOOPの重要な部分です。
  • デカップリングが改善されます。 find関数は、LinkedListクラスを処理できるようにするために、そのクラスについて知る必要はありません。メンバー関数として定義されている場合、それはLinkedListクラスのメンバーであり、基本的には2つの概念を1つの大きなblobにマージします。
  • 拡張性。クラスのインターフェースが「そのすべてのパブリックメンバー」だけでなく、「クラス上で動作するすべての非メンバー関数」でもあることを受け入れると、編集または変更することなく、クラスのインターフェースを拡張することが可能になります。クラス自体も再コンパイルします。

非メンバー関数を持つ機能は、C(他に選択の余地がなかった)に由来する可能性がありますが、最新のC++では、下位互換性の目的だけでなく、単純であるため、それ自体が重要な機能です。 、よりクリーンで再利用可能なコードです。

実際、C#はずっと後で同じことを実現したようです。なぜ拡張メソッドが追加されたと思いますか?これらは、Javaに似た単純な構文を維持しながら、上記を達成する試みです。ラムダも興味深い例です。ラムダも、特定のクラスのメンバーとしてではなく、自由に定義された本質的に小さな関数であるためです。つまり、非メンバー関数の概念は有用であり、C#の設計者も同じことを実現しています。彼らはバックドアからコンセプトを忍び込もうとしたところです。

http://www.ddj.com/cpp/184401197 および http://www.gotw.ca/publications/mill02.htm は、C++の専門家によって書かれた2つの記事です件名に。

39
jalf

非メンバー関数は良いことです カプセル化を改善するため で、型間の結合を減らします。 HaskellやF#などの最新のプログラミング言語は、無料の関数をサポートしています。

12

notの各メソッドを名前付きクラスに置くことの利点は何ですか?非メンバー関数がクラスのインターフェースを「汚染」するのはなぜですか?クラスのパブリックAPIの一部として使用したくない場合は、パブリックにしないか、そのクラスに配置しないでください。いつでも別のクラスを作成できます。

もちろん、無名関数(実際には同じではありません)を除いて、適切なスコープなしで浮かんでいるメソッドを書きたいと思ったことはありません。

つまり、メンバー以外の関数にはメリットがありませんが、適切な名前のクラスにすべてのメソッドを配置することで、一貫性、命名、およびドキュメントの面でメリットを得ることができます。

9
Jon Skeet
  • すべてのコードをクラス内に配置すると、より強力なリフレクション機能のセットが可能になります。
  • クラス内の静的メソッドが必要とするデータを初期化できる静的初期化子を使用できます。
  • 別のコンパイルユニットでは追加できないユニット内でメソッドを明示的に囲むことにより、メソッド間の名前の衝突を回避します。
3
Yuliy

CLS(共通言語仕様)は、CLSに準拠するライブラリーに非メンバー関数を含めるべきではないと述べています。これは、CLI(共通言語インターフェース)の基本的な制限に加えて、追加の制限セットのようなものです。

C#の将来のバージョンでは、クラス名の修飾なしでクラスの静的メンバーにアクセスできるようにするusingディレクティブを作成する機能が追加される可能性があります。

using System.Linq.Enumerable; // Enumerable is a static class

...

IEnumerable<int> range = Range(1, 10); // finds Enumerable.Range

その後、CLSと既存のライブラリを変更する必要はありません。

これらのブログ投稿はライブラリを示しています C#の関数型プログラミングの場合、静的メソッド呼び出しを修飾する必要があるために発生するノイズを削減するために、C#の関数型プログラミングでは1文字だけのクラス名を使用しています。 usingディレクティブがクラスをターゲットにできる場合、そのような例は少し良くなります。

3

Java以降、ほとんどのプログラマーは、どのメソッドもクラスのメンバーであることを簡単に受け入れています。言語をより簡単にするために、大きな障害を作らず、メソッドの概念をより狭くしています。

ただし、実際には、クラスはオブジェクトを推論し、オブジェクトは状態を推論するため、静的メソッドのみを含むクラスの概念は少し不合理に見えます。

2

C++はC#よりもはるかに複雑な言語です。そして、それらは構文的には似ているかもしれませんが、意味的には非常に異なる獣です。このように変更することはそれほど難しいことではないと思うかもしれませんが、私はそれがどのようになり得るかを見ることができました。 ANTLRには、 と呼ばれるwikiページがあり、言語の問題を難しくしているのは何ですか? これは、このような質問について相談するのに適しています。この場合:

文脈依存レクサー?解析している文の種類がわからない場合は、どのvocabulay記号を一致させるかを決定できません。

クラスで定義された関数について心配するだけでなく、クラスの外で定義された関数について心配する必要があります。概念的には、それほど大きな違いはありません。しかし、コードの字句解析と解析に関しては、「関数がクラスの外にある場合、関数はこの名前のないクラスに属します。ただし、クラスの内部にある場合、それはそのクラスに属します。クラス。"

また、コンパイラが次のようなメソッドに遭遇した場合:

public void Foo()
{
    Bar();
}

...今度は「バーはこのクラス内にありますか、それともグローバルクラスですか?」という質問に答える必要があります。

前方参照または外部参照?つまり、複数のパスが必要ですか? Pascalには、ファイル内のプロシージャ参照を処理するための「前方」参照がありますが、USES句などを介して他のファイル内のプロシージャを参照するには、特別な処理が必要です。

これは問題を引き起こすもう1つのことです。 C#は前方宣言を必要としないことに注意してください。コンパイラーは、1つのパスを作成して、名前が付けられているクラスと、それらのクラスに含まれる関数を判別します。ここで、クラスおよび関数を見つけることについて心配する必要があります。ここで、関数はクラスの内部または外部のいずれかになります。これは、C++パーサーがすべてを順番に解析するので、心配する必要がないものです。

誤解しないでください。おそらくC#で実行でき、おそらくそのような機能を使用します。しかし、静的メソッドの前にクラス名を入力するだけの場合、これらの障害を克服するすべてのトラブルに本当に価値があるのでしょうか。

1
Jason Baker

達成するためにメンバー以外の静的メソッドを作成したいことを本当に明確にする必要があると思います。

たとえば、必要なもののいくつかは Extension Methods で処理できます。

(静的メソッドのみを含むクラスの)もう1つの一般的な使用法は、ライブラリーでの使用です。この場合、完全に静的メソッドで構成されるアセンブリでクラスを作成してもほとんど害はありません。それらをまとめて保持し、名前の衝突を回避します。結局のところ、同じ目的を果たす静的メソッドがMathにあります。

また、C++のオブジェクトモデルをC#と比較する必要はありません。 C++は、クラスシステムをまったく備えていなかったCとほぼ(完全ではありませんが)互換性があります。そのため、C++は、特定の設計要件ではなく、Cのレガシーからこのプログラミングイディオムをサポートする必要がありました。

1
Cade Roux

無料の関数は、アヒルのタイピングと組み合わせると非常に便利です。 C++ STL全体がそれに基づいています。したがって、C#が本当のジェネリックを追加できた場合、無料の関数が導入されると確信しています。

経済学と同様に、言語デザインも心理学に関するものです。 C#の無料の関数を介して真のジェネリックの食欲を生み出し、提供しない場合は、C#を強制終了します。その後、すべてのC#開発者はC++に移行しますが、C#コミュニティではなく、C++に投資している人ではなく、誰もがそのようなことを望んでいません。

0

そのような関数を保持するクラス(たとえば、FreeFunctionsと呼ばれる静的クラス)が必要なのは事実ですが、using static FreeFunctions;は、関数を必要とするファイルの先頭に自由に配置できます。 FreeFunctions.修飾子でコードを散らかす必要があります。これが、関数定義をクラスに含める必要がないことよりも明らかに劣っているケースが実際にあるかどうかはわかりません。

0
Dylan Nicholson