C++ friendキーワード を使用すると、class A
がclass B
をそのフレンドとして指定できます。これにより、Class B
がclass A
のprivate
/protected
メンバーにアクセスできます。
これがC#(およびVB.NET)から除外された理由については何も読んだことがありません。これに対するほとんどの答え 以前のStackOverflowの質問 は、C++の有用な部分であり、それを使用する正当な理由があると言っているようです。私の経験では、同意する必要があります。
別の question は、C#アプリケーションでfriend
に似た処理を行う方法を本当に求めているようです。答えは一般にネストされたクラスを中心に展開しますが、friend
キーワードを使用するほどエレガントではないようです。
オリジナルの Design Patterns book は、例を通して定期的に使用しています。
要約すると、C#からfriend
が欠落しているのはなぜですか?また、C#でそれをシミュレートする「ベストプラクティス」の方法は何ですか?
(ところで、internal
キーワードはnotと同じもので、allinternal
メンバーにアクセスするためのアセンブリ全体のクラス。friend
を使用すると、特定のクラスに完全なアクセスを許可できますtoexactly one他のクラス)
プログラミングに友人がいることは、多かれ少なかれ「汚い」と見なされ、簡単に悪用されます。クラス間の関係を壊し、OO言語の基本的な属性を損ないます。
そうは言っても、それは素晴らしい機能であり、C++で何度も何度も使用しています。 C#でも使用したいと思います。しかし、C#の「純粋な」OOness(C++の擬似OOnessと比較して)のせいで、MSはJavaにフレンドキーワードC#がないため、(#冗談ではない)と判断しました。
深刻なことに注意してください。内部は友人ほど良くはありませんが、仕事を成し遂げます。 DLLを介さずにコードをサードパーティの開発者に配布することはまれです。あなたとあなたのチームが内部クラスとその使用について知っている限り、あなたは大丈夫です。
[〜#〜] edit [〜#〜] friendキーワードがOOPを弱体化させる方法を明確にしましょう。
プライベートおよび保護された変数とメソッドは、おそらくOOPの最も重要な部分の1つです。オブジェクトだけが使用できるデータまたはロジックを保持できるという考えにより、環境に依存しない機能の実装を記述できます。また、環境は処理に適さない状態情報を変更できないという考えです。 friendを使用すると、2つのクラスの実装を結合することになります。これは、インターフェースを結合しただけの場合よりもはるかに悪いことです。
サイドノートに。 friendを使用することはカプセル化に違反することではありませんが、逆に強制することです。アクセサー+ミューテーター、演算子のオーバーロード、パブリック継承、ダウンキャスト、などなど、多くの場合、誤用されていますが、キーワードにない、または悪化しているわけではありません、悪い目的。
他のスレッドの Konrad Rudolph の message を参照するか、C++ FAQの 関連エントリ を参照してください。
情報については、.NETのもう1つの関連する、しかしまったく同じものは[InternalsVisibleTo]
。これにより、アセンブリは、元のアセンブリの型/メンバーへの(内部)アクセスを(事実上)持つ別のアセンブリ(単体テストアセンブリなど)を指定できます。
friend
を使用すると、C++デザイナーはprivate *メンバーが誰に公開されるかを正確に制御できます。しかし、彼はすべてのプライベートメンバーを公開することを余儀なくされています。
internal
を使用すると、C#デザイナーは公開しているプライベートメンバーのセットを正確に制御できます。明らかに、彼は単一のプライベートメンバーを公開できます。ただし、アセンブリ内のすべてのクラスに公開されます。
通常、デザイナーは、いくつかのプライベートメソッドのみを、選択した他のいくつかのクラスに公開することを望んでいます。たとえば、クラスファクトリパターンでは、クラスC1はクラスファクトリCF1によってのみインスタンス化されることが望ましい場合があります。したがって、クラスC1には、保護されたコンストラクターとフレンドクラスファクトリCF1があります。
ご覧のとおり、カプセル化を破ることができる2つの次元があります。 friend
は1つの次元に沿って違反し、internal
は他の次元に沿って違反します。カプセル化の概念の違反はどれが悪いですか?言いにくい。ただし、friend
とinternal
の両方を使用できるようにすると便利です。さらに、これらの2つに追加するのは3番目のタイプのキーワードで、メンバーごとに使用され(internal
など)、ターゲットクラスを指定します(friend
など) 。
*簡潔にするために、「private and/or protected」ではなく「private」を使用します。
-ニック
C#のインターフェイスを使用することで、C++で「フレンド」が使用されるのと同じ種類のことを実現できるはずです。 2つのクラス間で渡されるメンバーを明示的に定義する必要があります。これは余分な作業ですが、コードを理解しやすくする場合もあります。
インターフェイスを使用してシミュレートできない「友人」の合理的な使用例がある場合は、共有してください。 C++とC#の違いをよりよく理解したいと思います。
実際、C#は、特別な単語なしでpure OOP方法で同じ動作をする可能性を与えます-それはプライベートインターフェイスです。
質問 友人に相当するC#とは何ですか? は、この記事の重複としてマークされており、本当に良い実現を提案する人はいません。両方の質問に対する答えをここに示します。
主なアイデアはここから取っていました: プライベートインターフェイスとは?
たとえば、別のクラスのインスタンスを管理し、それらに対していくつかの特別なメソッドを呼び出すことができるクラスが必要だとしましょう。このメソッドを他のクラスに呼び出す可能性を与えたくありません。これは、c ++の世界でfriend c ++キーワードが行うこととまったく同じです。
実際の良い例としては、一部のコントローラーが現在の状態オブジェクトを更新し、必要に応じて別の状態オブジェクトに切り替えるフルステートマシンパターンが考えられます。
あなたは出来る:
Controller.cs
public class Controller
{
private interface IState
{
void Update();
}
public class StateBase : IState
{
void IState.Update() { }
}
public Controller()
{
//it's only way call Update is to cast obj to IState
IState obj = new StateBase();
obj.Update();
}
}
Program.cs
class Program
{
static void Main(string[] args)
{
//it's impossible to write Controller.IState p = new StateBase();
//Controller.IState is hidden
StateBase p = new StateBase();
//p.Update(); //is not accessible
}
}
さて、継承はどうですか?
明示的なインターフェイスメンバーの実装はvirtualとして宣言できないため で説明されている手法を使用し、IStateをprotectedとしてマークして、Controllerから派生する可能性を与える必要があります。
Controller.cs
public class Controller
{
protected interface IState
{
void Update();
}
public class StateBase : IState
{
void IState.Update() { OnUpdate(); }
protected virtual void OnUpdate()
{
Console.WriteLine("StateBase.OnUpdate()");
}
}
public Controller()
{
IState obj = new PlayerIdleState();
obj.Update();
}
}
PlayerIdleState.cs
public class PlayerIdleState: Controller.StateBase
{
protected override void OnUpdate()
{
base.OnUpdate();
Console.WriteLine("PlayerIdleState.OnUpdate()");
}
}
最後に、クラスコントローラーが継承をスローする方法をテストする例:ControllerTest.cs
class ControllerTest: Controller
{
public ControllerTest()
{
IState testObj = new PlayerIdleState();
testObj.Update();
}
}
すべてのケースをカバーし、答えが役立ったことを願っています。
C#には、決定論的な破壊が欠落しているのと同じ理由で、「friend」キーワードが欠落しています。慣習を変えると、まるで彼らの新しい方法が他の誰かの古い方法よりも優れているかのように、人々は賢く感じます。それはすべて誇りです。
「友人のクラスは悪い」と言うのは、「gotoを使用しない」や「LinuxはWindowsよりも優れている」など、他の修飾されていない文と同じくらい近視眼的です。
プロキシクラスと組み合わせた「friend」キーワードは、特定のクラスに特定クラスの一部のみを公開するための優れた方法です。プロキシクラスは、他のすべてのクラスに対する信頼できる障壁として機能できます。 「パブリック」ではこのようなターゲティングは許可されません。概念的な「is a」関係が実際にない場合、継承で効果を得るために「保護」を使用するのは厄介です。
C#キーワード "internal" を使用すると、C++の "friend"に近づくことができます。
友人は単体テストを書くときに非常に役立ちます。
クラス宣言をわずかに汚染するという犠牲を払う一方で、クラスの内部状態を実際にテストすることをコンパイラが強制するリマインダーでもあります。
私が見つけた非常に便利できれいなイディオムは、ファクトリクラスがある場合であり、コンストラクターが保護されている作成するアイテムの友達になります。具体的には、レポートライターオブジェクトに一致するレンダリングオブジェクトを作成し、特定の環境にレンダリングする単一のファクトリーがあったときです。この場合、レポート作成者クラス(画像ブロック、レイアウトバンド、ページヘッダーなど)とそれらに一致するレンダリングオブジェクトとの関係についての単一の知識ポイントがあります。
これは、実際にはC#の問題ではありません。 ILの基本的な制限です。 C#は、検証可能な他の.Net言語と同様に、これによって制限されます。この制限には、C++/CLIで定義されたマネージクラスも含まれます( 仕様セクション20.5 )。
そうは言っても、ネルソンはなぜこれが悪いことなのかについて良い説明があると思います。
この制限の言い訳をやめます。友人は悪いですが、内部は良いですか?それらは同じものです。その友人だけが、誰にアクセスを許可し、誰に許可しないかをより正確に制御できます。
これは、カプセル化パラダイムを実施するためですか?アクセサーメソッドを記述する必要があります。 (クラスBのメソッドを除く)全員がこれらのメソッドを呼び出さないようにするにはどうすればよいですか? 「友人」がいないため、これも制御できないため、できません。
完璧なプログラミング言語はありません。 C#は私が見た中で最高の言語の1つですが、欠落している機能についてばかげた言い訳をしてもだれも助けにはなりません。 C++では、簡単なイベント/デリゲートシステム、リフレクション(+自動デ/シリアル化)、およびforeachを欠いていますが、C#では演算子のオーバーロードを欠いています(ええ、それは不要だと言ってください)、デフォルトパラメータ、constそれは回避できない、多重継承(そう、あなたはそれを必要としなかった、インターフェースは十分な代替であると私に言い続けてください)そしてメモリからインスタンスを削除することを決定する能力(いいえ、あなたがいない限りこれは恐ろしく悪くはありませんいじくり回し)
私は「友人」キーワードについて多くの賢明なコメントを読みましたが、それは有用なものであることに同意しますが、「内部」キーワードはあまり有用ではないと思います、そして両方とも純粋なOO 。
何があるの? (「友人」について言うと、「内部」についても言う)
はい;
「友人」を使用しないとコードが改善されますか?
Friendを使用すると、ローカルで問題が発生しますが、使用しないとコードライブラリユーザーに問題が発生します。
プログラミング言語の一般的な良い解決策は次のようになります:
// c++ style
class Foo {
public_for Bar:
void addBar(Bar *bar) { }
public:
private:
protected:
};
// c#
class Foo {
public_for Bar void addBar(Bar bar) { }
}
あなたはそれについてどう思いますか?最も一般的で純粋なオブジェクト指向ソリューションだと思います。任意のクラスに選択した任意のメソッドにオープンアクセスできます。
「方法」の質問にのみ答えます。
ここには非常に多くの答えがありますが、その機能を実現するための一種の「設計パターン」を提案したいと思います。以下を含む単純な言語メカニズムを使用します。
たとえば、学生と大学の2つの主要なクラスがあります。学生には、大学のみがアクセスできるGPAがあります。コードは次のとおりです。
public interface IStudentFriend
{
Student Stu { get; set; }
double GetGPS();
}
public class Student
{
// this is private member that I expose to friend only
double GPS { get; set; }
public string Name { get; set; }
PrivateData privateData;
public Student(string name, double gps) => (GPS, Name, privateData) = (gps, name, new PrivateData(this);
// No one can instantiate this class, but Student
// Calling it is possible via the IStudentFriend interface
class PrivateData : IStudentFriend
{
public Student Stu { get; set; }
public PrivateData(Student stu) => Stu = stu;
public double GetGPS() => Stu.GPS;
}
// This is how I "mark" who is Students "friend"
public void RegisterFriend(University friend) => friend.Register(privateData);
}
public class University
{
var studentsFriends = new List<IStudentFriend>();
public void Register(IStudentFriend friendMethod) => studentsFriends.Add(friendMethod);
public void PrintAllStudentsGPS()
{
foreach (var stu in studentsFriends)
Console.WriteLine($"{stu.Stu.Name}: stu.GetGPS()");
}
}
public static void Main(string[] args)
{
var Technion = new University();
var Alex = new Student("Alex", 98);
var Jo = new Student("Jo", 91);
Alex.RegisterFriend(Technion);
Jo.RegisterFriend(Technion);
Technion.PrintAllStudentsGPS();
Console.ReadLine();
}
.Net 3以降にInternalsVisibleToAttributeがありますが、ユニットテストの台頭後にテストアセンブリに対応するためだけに追加したのではないかと思われます。私はそれを使用する他の多くの理由を見ることができません。
これはアセンブリレベルで機能しますが、内部では機能しません。つまり、アセンブリを配布したいが、別の配布されていないアセンブリに特権アクセスを許可する場合です。
保護されたアセンブリと一緒に誰かがふりをする友人を作成することを避けるために、かなり正しいことは友人アセンブリに強いキーが必要です。
友人を使用することで物事が制御不能になる可能性があると示唆する人もいます。同意しますが、それはその有用性を低下させません。すべてのクラスメンバーを公開する以上に、友人が必ずしもOOパラダイムを傷つけることは確かではありません。確かに言語はすべてのメンバーを公開することを可能にしますが、それは規律あるプログラマです同様に、規律のあるプログラマーは、理にかなっている特定の場合に友人の使用を予約します。場合によっては、内部公開が多すぎると感じます。アセンブリ内のすべてにクラスまたはメソッドを公開するのはなぜですか。
自分のベースページを継承するASP.NETページがあり、そのページがSystem.Web.UI.Pageを継承しています。このページには、保護されたメソッドでアプリケーションのエンドユーザーエラーレポートを処理するコードがあります。
ReportError("Uh Oh!");
これで、ページに含まれるユーザーコントロールができました。ユーザーコントロールがページ内のエラーレポートメソッドを呼び出せるようにします。
MyBasePage bp = Page as MyBasePage;
bp.ReportError("Uh Oh");
ReportErrorメソッドが保護されている場合、それはできません。内部にすることはできますが、アセンブリ内のすべてのコードに公開されます。現在のページの一部であるUI要素(子コントロールを含む)に公開するだけです。具体的には、基本コントロールクラスでまったく同じエラー報告メソッドを定義し、基本ページでメソッドを呼び出すだけです。
protected void ReportError(string str) {
MyBasePage bp = Page as MyBasePage;
bp.ReportError(str);
}
おそらく属性として、言語を「OO」以下にすることなく、友人のような何かが有用であり、言語で実装され、クラスまたはメソッドを特定のクラスまたはメソッドの友人にすることができ、開発者が非常に提供できると信じています特定のアクセス。おそらく次のようなものです...(擬似コード)
[Friend(B)]
class A {
AMethod() { }
[Friend(C)]
ACMethod() { }
}
class B {
BMethod() { A.AMethod() }
}
class C {
CMethod() { A.ACMethod() }
}
私の前の例の場合、おそらく次のようなものがあります(セマンティクスを議論することはできますが、アイデアを理解しようとしているだけです):
class BasePage {
[Friend(BaseControl.ReportError(string)]
protected void ReportError(string str) { }
}
class BaseControl {
protected void ReportError(string str) {
MyBasePage bp = Page as MyBasePage;
bp.ReportError(str);
}
}
私が見るように、友人の概念は、物事を公開すること、またはメンバーにアクセスするためのパブリックメソッドまたはプロパティを作成することよりも、それに対するリスクはありません。友だちがデータのアクセシビリティの別のレベルの粒度を許可し、内部またはパブリックでそのアクセシビリティを広げるのではなく、そのアクセシビリティを狭めることができる場合。
プライベートのままにして、リフレクションを使用して関数を呼び出すことができます。テストフレームワークは、プライベート関数をテストするように要求する場合、これを行うことができます
私はそれがC#コンパイルモデルと関係があると思う-実行時にそれをコンパイルするJITを構築する。つまり、C#ジェネリックがC++ジェネリックと根本的に異なるのと同じ理由です。
フレンドシップは、インターフェイスと実装を分離することでシミュレートできます。アイデアは次のとおりです。「具体的なインスタンスが必要ですが、そのインスタンスの構築アクセスを制限します」。
例えば
_interface IFriend { }
class Friend : IFriend
{
public static IFriend New() { return new Friend(); }
private Friend() { }
private void CallTheBody()
{
var body = new Body();
body.ItsMeYourFriend(this);
}
}
class Body
{
public void ItsMeYourFriend(Friend onlyAccess) { }
}
_
ItsMeYourFriend()
はpublicであるにもかかわらず、Friend
クラスの具体的なインスタンスを他の誰も取得できないため、Friend
クラスのみがアクセスできます。ファクトリーNew()
メソッドはインターフェースを返しますが、プライベートコンストラクターがあります。
詳細については、私の記事 インターフェイスとコーディングを使用した無料のフレンドおよび内部インターフェイスメンバー を参照してください。
私は以前から友人を定期的に使用していましたが、OOPの違反または設計上の欠陥の兆候ではありません。適切な目的を達成するための最も効率的な手段はいくつかあります。最小限のコードで。
具体的な例の1つは、他のソフトウェアへの通信インターフェイスを提供するインターフェイスアセンブリを作成する場合です。一般に、プロトコルとピアの特性の複雑さを処理し、クライアントアプリとアセンブリ間でメッセージと通知を渡すことを含む比較的単純な接続/読み取り/書き込み/転送/切断モデルを提供するいくつかのヘビーウェイトクラスがあります。これらのメッセージ/通知はクラスでラップする必要があります。一般に、属性は作成者であるプロトコルソフトウェアによって操作される必要がありますが、多くのものは外界に対して読み取り専用のままにしておく必要があります。
OOPプロトコル/「作成者」クラスが作成されたすべてのクラスへの親密なアクセス権を持つために違反していることを宣言するのは愚かなことです。作成者クラスは私が最も重要だと思ったのは、「OOPの日本酒のためのOOP」モデルが通常もたらすBSの余分なコード行をすべて最小限に抑えることです。
属性、プロパティ、メソッドレベルで内部キーワードを適用できることを人々は知っていますか?それはトップレベルのクラス宣言のためだけではありません(ほとんどの例はそれを示すようです)。
Friendキーワードを使用するC++クラスがあり、それをC#クラスでエミュレートする場合:1. C#クラスpublicを宣言します2. C++で保護されているため、友人がアクセスできるすべての属性/プロパティ/メソッドを宣言しますC#3のinternal。すべての内部属性とプロパティへのパブリックアクセス用の読み取り専用プロパティを作成します。
私はそれが友人と100%同じではないことに同意し、ユニットテストは友人のようなものの必要性の非常に貴重な例です(プロトコルアナライザのロギングコードと同様)。ただし、内部は公開したいクラスへの公開を提供し、[InternalVisibleTo()]は残りを処理します-単体テストのために特別に生まれたようです。
友人に関しては、「どのクラスにアクセス権があるかを明示的に制御できるため、より良くなっている」ということです。そもそも、同じアセンブリで悪意のある疑いのあるクラスが何をしているのでしょうか。アセンブリを分割します!
C++で作業していて、friendキーワードを使用して自分自身を見つけた場合、クラスが他のクラスのプライベートメンバーにアクセスする必要があるのはなぜですか?
友情は、いくつかの内部クラスである「エージェント」を使用してシミュレートすることもできます。次の例を検討してください。
public class A // Class that contains private members
{
private class Accessor : B.BAgent // Implement accessor part of agent.
{
private A instance; // A instance for access to non-static members.
static Accessor()
{ // Init static accessors.
B.BAgent.ABuilder = Builder;
B.BAgent.PrivateStaticAccessor = StaticAccessor;
}
// Init non-static accessors.
internal override void PrivateMethodAccessor() { instance.SomePrivateMethod(); }
// Agent constructor for non-static members.
internal Accessor(A instance) { this.instance = instance; }
private static A Builder() { return new A(); }
private static void StaticAccessor() { A.PrivateStatic(); }
}
public A(B friend) { B.Friendship(new A.Accessor(this)); }
private A() { } // Private constructor that should be accessed only from B.
private void SomePrivateMethod() { } // Private method that should be accessible from B.
private static void PrivateStatic() { } // ... and static private method.
}
public class B
{
// Agent for accessing A.
internal abstract class BAgent
{
internal static Func<A> ABuilder; // Static members should be accessed only by delegates.
internal static Action PrivateStaticAccessor;
internal abstract void PrivateMethodAccessor(); // Non-static members may be accessed by delegates or by overrideable members.
}
internal static void Friendship(BAgent agent)
{
var a = BAgent.ABuilder(); // Access private constructor.
BAgent.PrivateStaticAccessor(); // Access private static method.
agent.PrivateMethodAccessor(); // Access private non-static member.
}
}
静的メンバーのみへのアクセスに使用する場合は、かなり単純になる可能性があります。このような実装の利点は、すべての型がフレンドシップクラスの内部スコープで宣言され、インターフェイスとは異なり、静的メンバーにアクセスできることです。
B.s.d.
友人は純粋なオブジェクト指向性を傷つけると言われました。私は同意します。
友人がカプセル化を助けることも述べられましたが、私も同意します。
OO方法論に友情を追加する必要がありますが、C++のように完全ではありません。私の友人クラスがアクセスできるフィールド/メソッドが欲しいのですが、私はしません。実際の生活と同じように、私は友人に個人の冷蔵庫へのアクセスを許可しますが、銀行口座へのアクセスは許可しません。
以下のように実装できます
class C1
{
private void MyMethod(double x, int i)
{
// some code
}
// the friend class would be able to call myMethod
public void MyMethod(FriendClass F, double x, int i)
{
this.MyMethod(x, i);
}
//my friend class wouldn't have access to this method
private void MyVeryPrivateMethod(string s)
{
// some code
}
}
class FriendClass
{
public void SomeMethod()
{
C1 c = new C1();
c.MyMethod(this, 5.5, 3);
}
}
もちろん、これはコンパイラの警告を生成し、インテリセンスを傷つけます。しかし、それは仕事をします。
余談ですが、自信のあるプログラマーは、プライベートメンバーにアクセスせずにテストユニットを実行する必要があると思います。これはまったく範囲外ですが、TDDについて読んでみてください。ただし、それでもやりたい場合(友人のようなc ++を持つ)のようなものを試してください
#if UNIT_TESTING
public
#else
private
#endif
double x;
したがって、UNIT_TESTINGを定義せずにすべてのコードを記述し、ユニットテストを実行する場合は、#define UNIT_TESTINGをファイルの最初の行に追加します(そして、ユニットテストを行うすべてのコードを#if UNIT_TESTINGの下で記述します)。それは慎重に処理する必要があります。
ユニットテストは友達の使用の悪い例だと思うので、友達が良いと思う理由の例を挙げたいと思います。破壊システム(クラス)があるとします。使用すると、破壊システムが摩耗し、改修する必要があります。今、あなたは免許を持つメカニックだけがそれを修正することを望んでいます。この例をささいなものにするために、メカニックは自分の個人用(プライベート)ドライバーを使用して修正すると言います。そのため、メカニッククラスは、breakingSystemクラスのフレンドである必要があります。