[C#] C #에서 C ++ 스타일의 ‘friend’키워드를 제공하지 않는 이유는 무엇입니까? [닫은]

C ++ 친구 키워드는 A가 있습니다 class A지정 class B의 친구로. 이것으로 /의 멤버에 Class B액세스 할 수 있습니다 .privateprotectedclass A

왜 이것이 C # (및 VB.NET)에서 제외되었는지에 대해서는 아무것도 읽지 못했습니다. 이 이전 StackOverflow 질문에 대한 대부분의 답변 은 C ++의 유용한 부분이라고 말하고 사용해야하는 이유가 있습니다. 내 경험상 나는 동의해야한다.

또 다른 질문friendC # 응용 프로그램에서 비슷한 작업을 수행하는 방법을 묻는 것 같습니다 . 대답은 일반적으로 중첩 클래스를 중심으로 이루어 지지만 friend키워드 를 사용하는 것만 큼 우아하지는 않습니다 .

원본 디자인 패턴 책 은 예제 전체에서 정기적으로 사용합니다.

요약하면 friendC #에서 누락 된 이유는 무엇이며 C #에서이를 시뮬레이션하는 “모범 사례”방법은 무엇입니까?

( internal키워드는 동일 하지 않으므로 전체 어셈블리 내의 모든 클래스가 internal멤버 에 액세스 friend할 수있는 반면 특정 클래스 에 정확히 하나의 다른 클래스에 대한 완전한 액세스 권한 을 부여 할 수 있습니다 )



답변

프로그래밍에 친구를 둔다는 것은 “더러운”것으로 간주되며 남용하기 쉽습니다. 클래스 간의 관계를 깨고 OO 언어의 일부 기본 속성을 약화시킵니다.

그것은 훌륭한 기능이며 C ++에서 여러 번 사용해 보았습니다. C #에서도 사용하고 싶습니다. 그러나 C #의 “순수한”OOness (C ++의 의사 OOness와 비교) 때문에 내기 MS는 Java에 친구 키워드가 없기 때문에 C #도 농담해서는 안된다고 결정했습니다.

심각한 메모 : 내부는 친구만큼 좋지 않지만 일을 끝내줍니다. DLL을 통하지 않고 타사 개발자에게 코드를 배포하는 경우는 드 rare니다. 귀하와 귀하의 팀이 내부 수업과 그 사용에 대해 알고있는 한 괜찮습니다.

편집 friend 키워드가 OOP를 어떻게 손상시키는 지 설명하겠습니다.

개인 및 보호 변수 및 방법은 아마도 OOP에서 가장 중요한 부분 중 하나 일 것입니다. 객체 만이 사용할 수있는 데이터 나 로직을 보유 할 수 있다는 아이디어를 통해 환경과 독립적으로 기능 구현을 작성할 수 있으며 환경에서 처리하기에 적합하지 않은 상태 정보를 변경할 수는 없습니다. 친구를 사용하면 두 클래스의 구현을 함께 결합 할 수 있습니다. 인터페이스를 결합하면 훨씬 나쁩니다.


답변

부수적으로. 친구 사용은 캡슐화를 위반하는 것이 아니라 반대로 캡슐화를 위반하는 것입니다. 접근 자 + 뮤 테이터, 연산자 오버로드, 퍼블릭 상속, 다운 캐스팅 등과 같이 종종 잘못 사용되지만 키워드에 나쁜 목적이 없거나 더 나쁜 것은 아닙니다.

참조 콘라드 루돌프메시지를 다른 스레드에서, 또는 당신이 선호하는 경우 참조 관련 항목 은 C ++ FAQ에 있습니다.


답변

정보를 위해 .NET에서 관련이 있지만 똑같지는 않은 또 다른 것은입니다 [InternalsVisibleTo]. 이는 어셈블리가 유형 / 멤버에 대한 “내부”액세스 권한을 가진 다른 어셈블리 (예 : 단위 테스트 어셈블리)를 지정할 수있게합니다. 원래 어셈블리.


답변

C #에서 인터페이스를 사용하여 C ++에서 “friend”와 동일한 종류의 작업을 수행 할 수 있어야합니다. 두 클래스간에 전달되는 멤버를 명시 적으로 정의해야합니다. 이는 추가 작업이지만 코드를 이해하기 쉽게 만들 수도 있습니다.

누군가 인터페이스를 사용하여 시뮬레이션 할 수없는 “친구”를 합리적으로 사용하는 예가 있다면 공유하십시오! C ++과 C #의 차이점을 더 잘 이해하고 싶습니다.


답변

으로 friend는 C ++ 디자이너는 개인 * 회원에 노출 누구를 정밀하게 제어 할 수 있습니다. 그러나 그는 개인 회원 모두를 공개해야했습니다.

internalC # 디자이너를 사용하면 노출중인 개인 구성원 집합을 정확하게 제어 할 수 있습니다. 분명히, 그는 단 하나의 개인 회원을 노출시킬 수 있습니다. 그러나 어셈블리의 모든 클래스에 노출됩니다.

일반적으로 설계자는 소수의 개인 메소드 만 선택된 소수의 다른 클래스에 노출하려고합니다. 예를 들어, 클래스 팩토리 패턴에서 클래스 C1은 클래스 팩토리 CF1에 의해서만 인스턴스화되는 것이 바람직 할 수 있습니다. 따라서 클래스 C1에는 보호 생성자와 친구 클래스 팩토리 CF1이있을 수 있습니다.

보시다시피, 캡슐화를 위반할 수있는 2 차원이 있습니다. friend한 차원을 따라 위반하고 다른 차원을 따라 위반합니다 internal. 캡슐화 개념에서 어느 것이 더 심각한 위반입니까? 말하기 어렵다. 그러나 모두가 좋을 것이다 friendinternal사용할 수 있습니다. 또한이 두 키워드를 추가하면 세 번째 유형의 키워드가되며 이는 멤버별로 사용되며 ( internal) 대상 클래스를 지정합니다 ( friend).

* 간결하게하기 위해 “개인 및 / 또는 보호”대신 “개인”을 사용합니다.

-닉


답변

실제로 C #은 특별한 단어없이 순수한 OOP 방식으로 동일한 동작을 얻을 수있는 가능성을 제공합니다. 전용 인터페이스입니다.

질문까지 친구의 C #은 무엇입니까? 이 기사와 중복되는 것으로 표시되어 있으며 실제로 좋은 실현을 제안하는 사람은 없습니다. 두 질문에 대한 답변을 여기에 표시하겠습니다.

주요 아이디어는 여기에서 가져 왔습니다. 개인 인터페이스 란 무엇입니까?

다른 클래스의 인스턴스를 관리하고 특별한 메소드를 호출 할 수있는 클래스가 필요하다고 가정 해 봅시다. 우리는이 메소드를 다른 클래스에 호출 할 가능성을 원하지 않습니다. 이것은 친구 C ++ 키워드가 C ++ 세계에서하는 것과 정확히 동일합니다.

실제로는 일부 컨트롤러가 현재 상태 객체를 업데이트하고 필요한 경우 다른 상태 객체로 전환하는 Full State Machine 패턴이 좋은 예라고 생각합니다.

당신은 할 수 있습니다 :

  • Update () 메소드를 공개하는 가장 쉽고 최악의 방법-모두가 왜 나쁜지 이해하기를 바랍니다.
  • 다음 방법은 내부로 표시하는 것입니다. 클래스를 다른 어셈블리에 넣으면 충분하지만 해당 어셈블리의 각 클래스는 각 내부 메서드를 호출 할 수 있습니다.
  • 개인 / 보호 인터페이스를 사용하십시오-나는이 길을 따랐습니다.

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 Controller.StateBase();
        //Controller.IState is hidden
        var p = new Controller.StateBase();
        //p.Update(); //is not accessible
    }
}

음, 상속은 어떻습니까?

명시 적 인터페이스 멤버 구현을 가상으로 선언 할 수 없으며 IState를 보호 된 것으로 표시하여 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()");
    }
}

마지막으로 클래스 컨트롤러 throw 상속을 테스트하는 방법의 예 :
ControllerTest.cs

class ControllerTest: Controller
{
    public ControllerTest()
    {
        IState testObj = new PlayerIdleState();
        testObj.Update();
    }
}

모든 경우를 다루고 내 답변이 도움이 되었기를 바랍니다.


답변

친구는 단위 테스트를 작성할 때 매우 유용합니다.

클래스 선언을 약간 오염시키는 비용이 들지만, 클래스의 내부 상태에 대해 실제로 어떤 테스트가 신경 쓰일 지에 대해 컴파일러에서 상기시켜줍니다.

내가 찾은 매우 유용하고 깨끗한 관용구는 팩토리 클래스가있을 때 보호 된 생성자가있는 항목을 친구로 만드는 것입니다. 보다 구체적으로 말하면, 보고서 작성기 개체에 대해 일치하는 렌더링 개체를 만들어 지정된 환경으로 렌더링하는 단일 팩토리가있을 때였습니다. 이 경우 보고서 작성기 클래스 (그림 블록, 레이아웃 밴드, 페이지 머리글 등)와 일치하는 렌더링 개체 간의 관계에 대한 단일 지식이 있습니다.