C#과 동등한 친구 클래스

Abdul Mateen 2023년10월12일
  1. C++의 프렌드 클래스
  2. Friend 클래스와 동등한 C# 사용
C#과 동등한 친구 클래스

이 자습서에서는 C#의 friend 클래스에 해당하는 항목에 대해 설명합니다. 먼저 프렌드 클래스와 C++ 예제를 사용하여 프로그래밍에 사용하는 방법에 대해 설명합니다.

다음으로 C#에 해당하는 friend 클래스가 없는 경우 대안을 살펴보겠습니다.

C++의 프렌드 클래스

객체 지향 프로그래밍에서 주로 적용되는 중요한 프로그래밍 기능은 데이터 은닉입니다. 클래스 외부에서 데이터 멤버를 숨기는 것은 클래스 내에서만 데이터 멤버를 업데이트할 수 있음을 의미합니다.

따라서 오직 하나의 클래스(데이터 멤버 보유)만이 데이터 멤버의 값을 변경할 수 있다고 말할 수 있습니다.

데이터를 숨기면 수정 중인 클래스를 테스트해야 하므로 테스트 범위가 줄어듭니다. 다른 클래스는 일부 클래스의 개인 데이터 멤버에 액세스할 수 없습니다. 따라서 코드가 수정되지 않은 경우 테스트할 필요가 없습니다.

때때로 데이터 은닉은 길고 복잡한 코드에서 약간 손상됩니다. 클래스는 일부 함수 또는 클래스를 친구로 선언할 수 있습니다.

이에 대한 응답으로 친구 기능과 친구 클래스는 개인 데이터 멤버에 액세스할 수 있습니다.

우정의 구문은 다음과 같습니다.

class ABC {
  ... friend class XYZ;
  friend int change_data_member(int, char);
  ...

클래스 ABC는 라인 #3에서 클래스 XYZ를 친구로 선언합니다. 4번 줄은 friend 메서드를 선언하는 구문을 보여줍니다.

여기서 ABC 클래스는 change_data_member 메서드를 friend 함수로 선언합니다.

ABC 클래스로부터 우정 플래그를 수신한 후 XYZ 클래스의 모든 함수는 ABC 클래스의 개인 데이터 멤버에 액세스할 수 있습니다.

다만, 기능에 친목을 부여하는 경우에는 친목의 범위가 제한됩니다. 친구 기능(즉, 친구로 선언됨)만이 우정 부여 클래스 구성원의 개인 데이터에 액세스할 수 있습니다.

C++에서 우정 개념의 구현을 봅시다.

#include <iostream>
using namespace std;
class ABC {
  int privateMember;

 public:
  ABC(int privateMember) { this->privateMember = privateMember; }
  friend int getPrivateMember(ABC &);
  friend void setPrivateMember(ABC &, int);
};
int getPrivateMember(ABC &obj) { return obj.privateMember; }
void setPrivateMember(ABC &obj, int privateMember) {
  obj.privateMember = privateMember;
}
int main() {
  ABC abc(5);
  cout << "Private Member: " << getPrivateMember(abc) << '\n';
  setPrivateMember(abc, 7);
  cout << "Private Member: " << getPrivateMember(abc) << '\n';
  return 0;
}

이 코드에서 클래스 ABC에는 프라이빗 데이터 멤버 privateMember가 있습니다. 클래스 본문의 마지막 두 줄에서 두 개의 비클래스 함수인 getPrivateMembersetPrivateMember에 우정이 부여됩니다.

이 코드의 출력은 다음과 같습니다.

Private Member: 5
Private Member: 7

프라이빗 데이터 멤버는 입력 및 출력 모두에 대해 액세스됩니다. 다음은 친구 클래스의 예입니다.

#include <iostream>
using namespace std;
class ABC {
  int privateMember;
  friend class DEF;
};
class DEF {
  ABC abc;

 public:
  int getPrivateMember() { return abc.privateMember; }
  void setPrivateMember(int privateMember) {
    abc.privateMember = privateMember;
  }
};
int main() {
  DEF def;
  def.setPrivateMember(12);
  cout << "Private Member: " << def.getPrivateMember() << '\n';
  return 0;
}

ABC 클래스의 두 번째 줄에서 DEF 클래스에 우정이 부여됩니다. 우정 상태가 있으면 DEF 클래스의 getPrivateMembersetPrivateMember 메소드는 ABC 클래스의 개인 구성원에 액세스할 수 있습니다.

이 코드의 출력은 다음과 같습니다.

Private Member: 12

친구 함수 또는 친구 클래스 개념은 C#이 아닌 C++에서 사용할 수 있습니다. 그러나 멤버가 아닌 데이터 함수에 액세스할 수 있는 몇 가지 방법이 있습니다.

Friend 클래스와 동등한 C# 사용

C#의 일부 비멤버 함수에서 모든 클래스의 데이터 멤버에 액세스하는 다양한 방법이 있습니다. 이에 대해 하나씩 논의해 보자.

중첩 클래스 사용

C++ 친구 클래스의 가장 가까운 C# 동등/대체는 중첩/내부 클래스를 생성하는 것입니다. 중첩 클래스는 외부 클래스의 전용 데이터 멤버에 액세스할 수 있습니다.

다음 예제에서는 Inner_class 클래스를 Outer_class에 대한 중첩 클래스로 만듭니다. 포함하는 클래스의 범위 내에 있는 Inner_classOuter_class의 개인 멤버에 액세스할 수 있으므로 friend 클래스를 모방합니다.

using System;
public class Outer_class {
  int privateMember;
  Outer_class(int privateMember) {
    this.privateMember = privateMember;
  }
  public class Inner_class {
    public void innerClassFunction() {
      Outer_class obj = new Outer_class(5);
      Console.WriteLine("Private Member: " + obj.privateMember);
    }
  }
}
public class Driver {
  static public void Main() {
    Outer_class.Inner_class obj = new Outer_class.Inner_class();
    obj.innerClassFunction();
  }
}

위의 코드에는 하나의 외부 클래스와 하나의 내부 클래스가 있습니다. privateMember 변수는 외부 클래스의 전용 멤버입니다.

Inner_class 내부에서 외부 클래스의 privateMember인 개인 데이터 멤버에 액세스하고 있습니다.

다음으로 Inner_class 개체를 생성한 Driver 클래스가 있습니다. Inner_class 개체를 사용하여 Inner_class 내부에서 innerClassFunction을 호출합니다.

출력:

Private Member: 5

위의 코드는 문제 없이 빌드 및 실행되지만 여전히 아래 예제와 같이 부분 클래스 개념을 사용하여 Outer_classInner_class를 구분하는 것이 좋습니다.

using System;
public partial class Outer_class {
  int privateMember;
  Outer_class(int privateMember) {
    this.privateMember = privateMember;
  }
}
public partial class Outer_class {
  public class Inner_class {
    public void innerClassFunction() {
      Outer_class obj = new Outer_class(5);
      Console.WriteLine("Private Member: " + obj.privateMember);
    }
  }
}
public class Driver {
  static public void Main() {
    Outer_class.Inner_class obj = new Outer_class.Inner_class();
    obj.innerClassFunction();
  }
}

Outer_class의 두 번째 부분 클래스 정의에는 innerClassFunction 함수가 있습니다. 이 innerClassFunction 메소드는 외부 클래스의 객체를 생성합니다.

Outer_class 개체를 생성하는 동안 생성자 매개 변수에 대한 인수로 5를 전달합니다. 이 생성자 매개변수는 아래 출력에서 볼 수 있듯이 외부 클래스의 첫 번째 부분 정의에서 데이터 멤버의 값이 됩니다.

Private Member: 5

부분 클래스는 여러 사람이 클래스의 다른 부분을 작성하는 대규모 프로젝트에서 중요합니다.

여기에서 우리는 이 부분 클래스 옵션을 이용하고 외부 클래스의 또 다른 부분 부분으로 내부에 Inner_class를 작성하고 있습니다. 이 부분 체계는 부분 단위를 분리하여 유지할 수 있지만 단일 단위로 사용할 수 있습니다.

이 체계의 주요 이점은 실제 클래스 정의가 Inner_class 정의를 작성하는 사람에게 노출되지 않는다는 것입니다.

반사 객체

Reflection 객체를 사용하여 런타임 유형 정보를 얻을 수 있습니다.

프로그램 실행 중에 Reflection 객체를 사용하여 Type 정보를 얻을 수 있습니다. System.Reflection 네임스페이스는 실행 중인 프로그램의 메타데이터에 액세스하는 데 사용할 수 있습니다.

System.Reflection은 런타임에 유용한 정보를 제공하고 다양한 유형, 값 및 클래스 개체를 프로그램에 추가하는 데 도움이 됩니다.

또한 System.Reflection 네임스페이스를 사용하여 메서드에 대한 핸들을 얻고 클래스의 전용 메서드를 테스트할 수 있습니다.

이런 식으로 클래스의 전용 메서드를 호출할 수 있습니다. 따라서 friend 클래스를 복제할 수 있습니다.

예를 들어 개인 메서드 callable_fun()이 있는 다음 클래스를 가정합니다.

public class Class1 {
  private char callable_fun() {
    return 'C';
  }
}

개인 함수 callable_fun을 테스트하려면 다음 코드를 작성해야 합니다.

using System.Reflection;
public class Driver {
  static public void Main() {
    Class1 c = new Class1();
    Type class1Type = c.GetType();
    MethodInfo callMeMethod =
        class1Type.GetMethod("callable_fun", BindingFlags.Instance | BindingFlags.NonPublic);
    char result = (char)callMeMethod.Invoke(c, null);
    Console.WriteLine("Output:" + result);
  }
}

이 코드의 출력은 다음과 같습니다.

Output:C

Visual Studio Team System을 사용하는 경우 메서드를 마우스 오른쪽 버튼으로 클릭하고 Create Unit Tests...를 선택하여 VS가 프라이빗 접근자로 프록시 클래스를 자동으로 생성하도록 할 수 있습니다.

InternalsVisibleToAttribute 클래스 사용

이 클래스는 일반적으로 현재 어셈블리 내에서만 볼 수 있는 유형에 대해 지정된 어셈블리에 대한 가시성을 향상시키는 데 도움이 됩니다.

// Syntax
[System.AttributeUsage(System.AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)]
public sealed class InternalsVisibleToAttribute : Attribute

두 개의 서명되지 않은 어셈블리가 있는 경우 InternalsVisibleToAttribute 클래스는 다른 서명되지 않은 어셈블리에서 하나의 서명되지 않은 어셈블리의 전용 멤버에 액세스하는 데 도움이 됩니다. 한 어셈블리의 이름(friend 클래스/어셈블리의 이름을 가짐)이 인수로 전달됩니다.

다음으로 InternalsVisibleToAttribute가 서명되지 않은 다른 어셈블리의 전용 멤버에 액세스하는 데 사용되는 예를 살펴보겠습니다. 여기서 SecondClassFirstClass의 전용 멤버에 액세스합니다.

아래 코드를 참조하십시오.

using System;
using System.Runtime.CompilerServices;
using Utilities.Example;

[assembly:InternalsVisibleToAttribute("FirstClass")]

namespace Utilities.Example {
  public class FirstClass {
    internal static int InnerMethod(int x) {
      return x * x;
    }
  }
}

다음 예제는 SecondClass 어셈블리의 소스 코드를 제공합니다.

public class SecondClass {
  public static void Main() {
    Console.WriteLine(FirstClass.InnerMethod(10));
  }
}

이 코드의 출력은 100입니다.

서명된 어셈블리가 있는 경우 디렉터리 경로와 파일 이름 확장명이 필요하지 않습니다.

다음 예제에서는 InternalsVisibleToAttribute를 사용하여 다른 서명된 어셈블리에 표시되는 서명된 어셈블리에서 innerMethod라는 프라이빗 메서드를 호출합니다. 내부 innerMethod 메서드를 포함하는 FirstClass 클래스를 정의합니다.

using System;
using System.IO;
using System.Runtime.CompilerServices;

[assembly:InternalsVisibleTo(
    "Friend_Assembly, PublicKey=002400000480000094" + "0000000602000000240000525341310004000" +
    "001000100bf8c25fcd44838d87e245ab35bf7" + "3ba2615707feea295709559b3de903fb95a93" +
    "3d2729967c3184a97d7b84c7547cd87e435b5" + "6bdf8621bcb62b59c00c88bd83aa62c4fcdd4" +
    "712da72eec2533dc00f8529c3a0bbb4103282" + "f0d894d5f34e9f0103c473dce9f4b457a5dee" +
    "fd8f920d8681ed6dfcb0a81e96bd9b176525a" + "26e0b3")]

public class FirstClass {
  internal static int innerMethod(int x) {
    return x * x;
  }
}

다음 예제가 Friend_Assembly라는 강력한 이름의 어셈블리로 컴파일된다고 가정합니다. FirstClass의 내부 메소드는 innerMethod를 성공적으로 호출하지만 메소드는 FirstClass 내부에 있습니다.

public class SecondClass {
  public static void Main() {
    int result = FirstClass.innerMethod(10);
    Console.WriteLine(result);
  }
}

이 코드의 출력은 다시 100입니다.

우리는 C#에서 C++ friend 클래스의 다른 등가물을 제시했습니다. 독자는 요구 사항에 따라 무엇이든 사용할 수 있습니다.

관련 문장 - Csharp Class