IT story

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

hot-time 2020. 5. 6. 21:07
반응형

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

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


C # 키워드 "internal"을 사용 하여 C ++ "friend"에 가까이 갈 수 있습니다 .


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

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

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


C #에 결정적인 파괴가없는 것과 같은 이유로 "friend"키워드가 없습니다. 새로운 방식이 다른 사람의 기존 방식보다 우월한 것처럼 컨벤션을 변경하면 사람들이 똑똑해집니다. 그것은 자부심에 관한 것입니다.

"친구 클래스가 나쁘다"고 말하는 것은 "goto를 사용하지 마십시오"또는 "Linux가 Windows보다 낫다"와 같은 다른 규정되지 않은 문장만큼이나 근시안적입니다.

프록시 클래스와 결합 된 "friend"키워드 는 클래스의 특정 부분 다른 클래스에 노출시키는 좋은 방법 입니다. 프록시 클래스는 다른 모든 클래스에 대한 신뢰할 수있는 장벽 역할을 할 수 있습니다. "공개"는 그러한 타겟팅을 허용하지 않으며, "보호"를 사용하여 실질적으로 개념적 "is"관계가 없다면 상속 효과를 얻는 것이 어색합니다.


실제로 C #에서는 문제가되지 않습니다. IL의 근본적인 한계입니다. C #은 확인할 수있는 다른 .Net 언어와 마찬가지로 이에 제한됩니다. 이 제한에는 C ++ / CLI에 정의 된 관리 클래스도 포함됩니다 ( 스펙 섹션 20.5 ).

나는 넬슨이 왜 이것이 나쁜지에 대해 잘 설명했다고 생각합니다.


이 제한에 대한 변명을 그만두십시오. 친구는 나쁘지만 내부는 좋습니까? 그들은 같은 것입니다, 오직 그 친구 만이 당신이 접근 할 수있는 사람과 그렇지 않은 사람을보다 정확하게 통제 할 수 있습니다.

이것은 캡슐화 패러다임을 강화하는 것입니까? 접근 자 메서드를 작성해야합니다. 이제 무엇을해야합니까? B 클래스의 메소드를 제외한 모든 사람이 이러한 메소드를 호출하지 못하게하려면 어떻게해야합니까? "친구"가 없어서 제어 할 수 없기 때문에 할 수 없습니다.

완벽한 프로그래밍 언어는 없습니다. C #은 내가 본 최고의 언어 중 하나이지만 누락 된 기능에 대해 바보 같은 변명을하는 것은 아무도 도움이되지 않습니다. C ++에서는 쉬운 이벤트 / 위임 시스템, 리플렉션 (+ 자동 de / serialization) 및 foreach가 누락되었지만 C #에서는 연산자 오버로드 (예, 필요하지 않다는 것을 계속 알려줍니다), 기본 매개 변수, const 우회 할 수없는, 다중 상속 (예, 필요하지 않고 인터페이스가 충분히 대체되었다고 계속 말하십시오) 및 메모리에서 인스턴스를 삭제하기로 결정하는 기능 (아니요, 그렇지 않으면 엄청나게 나쁘지 않습니다. 땜장이)


.Net 3부터 InternalsVisibleToAttribute가 있지만 단위 테스트가 끝난 후 어셈블리를 테스트하기 위해 추가하기 위해 추가 한 것 같습니다. 그것을 사용해야하는 다른 많은 이유를 볼 수 없습니다.

어셈블리 수준에서 작동하지만 내부가 그렇지 않은 작업을 수행합니다. 즉, 어셈블리를 배포하려고하지만 배포되지 않은 다른 어셈블리가 권한있는 액세스 권한을 갖기를 원하는 위치입니다.

아주 잘 맞아 그들은 누군가 보호 된 어셈블리와 함께 친구를 만드는 것을 피하기 위해 친구 어셈블리를 강력하게 키워야합니다.


나는 "friend"키워드에 대한 많은 현명한 의견을 읽었으며 그것이 유용한 것에 동의하지만 "internal"키워드가 덜 유용하다고 생각하고 순수한 OO 프로그래밍에는 여전히 나쁘다.

우리가 가진 것? ( "친구"에 대해서도 말하고 "내부"에 대해서도 말하고 있습니다)

  • "friend"를 사용하면 코드가 덜 순수 해 지나요?
  • 예;

  • "friend"를 사용하지 않으면 코드가 향상됩니까?

  • 아니요, 우리는 여전히 클래스간에 사적인 관계를 만들어야하며, 아름다운 캡슐화를 깨뜨릴 때만 할 수 있으므로 좋지 않습니다. "친구"를 사용하는 것보다 더 악한 것을 말할 수 있습니다.

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) { }
}

당신이 그것에 대해 어떻게 생각하십니까? 가장 일반적이고 순수한 객체 지향 솔루션이라고 생각합니다. 원하는 수업에 선택한 방법을 열 수 있습니다.


나는 "어떻게"질문에만 대답 할 것이다

여기에는 많은 답변이 있지만 그 기능을 달성하기 위해 일종의 "디자인 패턴"을 제안하고 싶습니다. 다음과 같은 간단한 언어 메커니즘을 사용합니다.

  • 인터페이스
  • 중첩 클래스

예를 들어, 학생과 대학의 두 가지 주요 수업이 있습니다. 학생에게는 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();
}

C # 컴파일 모델과 관련이 있다고 생각합니다 .IL은 런타임에 JIT를 컴파일하는 IL을 빌드합니다. 즉, C # 제네릭이 C ++ 제네릭과 근본적으로 다른 동일한 이유입니다.


비공개로 유지하고 리플렉션을 사용하여 함수를 호출 할 수 있습니다. 테스트 프레임 워크는 개인 함수를 테스트하도록 요청하면이 작업을 수행 할 수 있습니다


나는 정기적으로 친구를 사용했지만 OOP 위반이나 디자인 결함의 징후라고 생각하지 않습니다. 가장 적은 양의 코드로 올바른 목적을 달성하기위한 가장 효율적인 수단 인 곳이 몇 군데 있습니다.

한 가지 구체적인 예는 다른 소프트웨어에 대한 통신 인터페이스를 제공하는 인터페이스 어셈블리를 만들 때입니다. 일반적으로 프로토콜 및 피어 특성의 복잡성을 처리하고 클라이언트 앱과 어셈블리간에 메시지 및 알림을 전달하는 비교적 간단한 연결 / 읽기 / 쓰기 / 전달 / 연결 끊기 모델을 제공하는 몇 가지 무거운 클래스가 있습니다. 이러한 메시지 / 알림은 클래스로 래핑해야합니다. 속성은 일반적으로 프로토콜 소프트웨어가 생성자이므로 프로토콜 소프트웨어로 조작해야하지만 많은 것들이 외부 세계에 대해 읽기 전용으로 남아 있어야합니다.

protocol / "creator"클래스가 생성 된 모든 클래스에 대해 친밀한 액세스 권한을 갖는 것은 OOP 위반임을 선언하는 것은 어리석은 일입니다. 제작자 클래스는 모든 데이터 비트를 조금씩 바꿔야했습니다. 내가 가장 중요하게 생각한 것은 "OOP for OOP Sake"모델이 일반적으로 이끄는 모든 BS 추가 코드 줄을 최소화하는 것입니다. 여분의 스파게티는 더 많은 버그를 만듭니다.

사람들은 속성, 속성 및 메서드 수준에서 내부 키워드를 적용 할 수 있다는 것을 알고 있습니까? 최상위 클래스 선언만을위한 것이 아닙니다 (대부분의 예제는 그것을 보여줍니다).

friend 키워드를 사용하는 C ++ 클래스가 있고이를 C # 클래스에서 에뮬레이션하려면 다음을 수행하십시오. 1. C # 클래스를 public으로 선언하십시오. 2. C ++로 보호되고 따라서 친구가 액세스 할 수있는 모든 속성 / 속성 / 방법을 선언하십시오. C #의 internal 3. 모든 내부 속성 및 속성에 대한 공개 액세스를위한 읽기 전용 속성 만들기

나는 친구와 100 % 동일하지 않다는 것에 동의하며, 단위 테스트는 프로토콜 분석기 로깅 코드와 마찬가지로 친구와 같은 것이 필요하다는 매우 귀중한 예입니다. 그러나 internal은 노출하려는 클래스에 노출을 제공하며 [InternalVisibleTo ()]는 나머지를 처리합니다. 단위 테스트를 위해 특별히 만들어진 것처럼 보입니다.

As far as friend "being better because you can explicitely control which classes have access" -- what in heck are a bunch of suspect evil classes doing in the same assembly in the first place? Partition your assemblies!


The friendship may be simulated by separating interfaces and implementations. The idea is: "Require a concrete instance but restrict construction access of that instance".

For example

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) { }
}

In spite of the fact that ItsMeYourFriend() is public only Friend class can access it, since no one else can possibly get a concrete instance of the Friend class. It has a private constructor, while the factory New() method returns an interface.

See my article Friends and internal interface members at no cost with coding to interfaces for details.


일부는 친구를 사용하여 상황을 통제 할 수 없다고 제안했습니다. 나는 동의하지만 그것의 유용성은 줄어들지 않습니다. 친구가 반드시 모든 반원을 공개하는 것 이상으로 OO 패러다임을 다치게 할 것이라고 확신하지 않습니다. 확실히이 언어를 사용하면 모든 멤버를 공개 할 수 있지만, 이러한 유형의 디자인 패턴을 피하는 훈련 된 프로그래머입니다. 마찬가지로, 훈련 된 프로그래머는 이해가되는 특정한 경우에 친구를 사용할 수 있습니다. 어떤 경우에는 내부 노출이 너무 많이 느껴집니다. 왜 클래스 나 메소드를 어셈블리의 모든 것에 노출해야합니까?

내 자신의 기본 페이지를 상속하고 차례로 System.Web.UI.Page를 상속하는 ASP.NET 페이지가 있습니다. 이 페이지에는 응용 프로그램에 대한 최종 사용자 오류보고를 보호되는 방법으로 처리하는 코드가 있습니다.

ReportError("Uh Oh!");

Now, I have a user control that is contained in the page. I want the user control to be able to call the error reporting methods in the page.

MyBasePage bp = Page as MyBasePage;
bp.ReportError("Uh Oh");

It can't do that if the ReportError method is protected. I can make it internal, but it is exposed to any code in the assembly. I just want it exposed to the UI elements that are part of the current page (including child controls). More specifically, I want my base control class to define the exact same error reporting methods, and simply call methods in the base page.

protected void ReportError(string str) {
    MyBasePage bp = Page as MyBasePage;
    bp.ReportError(str);
}

I believe that something like friend could be useful and implemented in the language without making the language less "OO" like, perhaps as attributes, so that you can have classes or methods be friends to specific classes or methods, allowing the developer to provide very specific access. Perhaps something like...(pseudo code)

[Friend(B)]
class A {

    AMethod() { }

    [Friend(C)]
    ACMethod() { }
}

class B {
    BMethod() { A.AMethod() }
}

class C {
    CMethod() { A.ACMethod() }
}

In the case of my previous example perhaps have something like the following (one can argue semantics, but I'm just trying to get the idea across):

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);
    }
}

As I see it, the friend concept has no more risk to it than making things public, or creating public methods or properties to access members. If anything friend allows another level of granularity in accessibility of data and allows you to narrow that accessibility rather than broadening it with internal or public.


If you are working with C++ and you find your self using friend keyword, it is a very strong indication, that you have a design issue, because why the heck a class needs to access the private members of other class??


B.s.d.

It was stated that, friends hurts pure OOness. Which I agree.

It was also stated that friends help encapsulation, which I also agree.

I think friendship should be added to the OO methodology, but not quite as it in C++. I'd like to have some fields/methods that my friend class can access, but I'd NOT like them to access ALL my fields/methods. As in real life, I'd let my friends access my personal refrigerator but I'd not let them to access my bank account.

One can implement that as followed

    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);
        }
    }

That will of course generate a compiler warning, and will hurt the intellisense. But it will do the work.

On a side note, I think that a confident programmer should do the testing unit without accessing the private members. this is quite out of the scope, but try to read about TDD. however, if you still want to do so (having c++ like friends) try something like

#if UNIT_TESTING
        public
#else
        private
#endif
            double x;

so you write all your code without defining UNIT_TESTING and when you want to do the unit testing you add #define UNIT_TESTING to the first line of the file(and write all the code that do the unit testing under #if UNIT_TESTING). That should be handled carefully.

Since I think that unit testing is a bad example for the use of friends, I'd give an example why I think friends can be good. Suppose you have a breaking system (class). With use, the breaking system get worn out and need to get renovated. Now, you want that only a licensed mechanic would fix it. To make the example less trivial I'd say that the mechanic would use his personal (private) screwdriver to fix it. That's why mechanic class should be friend of breakingSystem class.


The friendship may also be simulated by using "agents" - some inner classes. Consider following example:

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.
  }
}

It could be alot simpler when used for access only to static members. Benefits for such implementation is that all the types are declared in the inner scope of friendship classes and, unlike interfaces, it allows static members to be accessed.

참고URL : https://stackoverflow.com/questions/203616/why-does-c-sharp-not-provide-the-c-style-friend-keyword

반응형