C #에서 "내부"키워드의 실제 사용


420

internalC # 에서 키워드 의 실제 사용법이 무엇인지 설명해 주 시겠습니까?

것을 나는 알고 internal현재 어셈블리에 대한 수정 액세스를 제한,하지만 나는 그것을 언제 어떤 상황에서 사용해야합니까?

답변:


379

동일한 어셈블리 내의 다른 많은 클래스에서 액세스하려고하지만 다른 어셈블리의 코드에 액세스 할 수 없도록하려는 유틸리티 또는 도우미 클래스 / 방법.

에서 MSDN (archive.org를 통해) :

내부 액세스의 일반적인 사용은 구성 요소 기반 개발에 있으며, 이는 구성 요소 그룹이 나머지 응용 프로그램 코드에 노출되지 않고 개인 방식으로 협력 할 수 있기 때문입니다. 예를 들어, 그래픽 사용자 인터페이스를 구축하기위한 프레임 워크는 내부 액세스 권한이있는 멤버를 사용하여 협력하는 Control 및 Form 클래스를 제공 할 수 있습니다. 이러한 멤버는 내부에 있으므로 프레임 워크를 사용하는 코드에 노출되지 않습니다.

InternalsVisibleTo어셈블리 레벨 속성 과 함께 내부 수정자를 사용 하여 대상 어셈블리 내부 클래스에 대한 특별 액세스 권한이 부여 된 "친구"어셈블리를 작성할 수도 있습니다.

이는 유닛 테스트 어셈블리를 생성 한 다음 테스트 할 어셈블리의 내부 멤버를 호출 할 수 있도록하는 데 유용 할 수 있습니다. 물론 다른 어셈블리에는이 수준의 액세스 권한이 부여되지 않으므로 시스템을 해제하면 캡슐화가 유지됩니다.


8
InternalsVisibleTo 특성은 큰 도움이됩니다. 감사합니다.
erict

33
내 느낌은 당신이 내부를 필요로하는 순간부터 어딘가에 디자인에 문제가 있다는 것입니다. IMHO, 모든 문제를 해결하기 위해 순수 OO를 사용할 수 있어야합니다. 바로 지금, 나는 .NET Framework 소스에서 내부적으로 백만 번째를 사용하는 것을 쳐다 보면서 자신이 사용하는 것을 활용하는 방법을 배제했습니다. 내부를 사용하여 표면적으로 모듈 식이지만 물건을 분리하려고 할 때 분해되는 단일체를 만들었습니다. 또한 구조에 대한 반성이 있기 때문에 보안은 논쟁이 아닙니다.
절망의 그리로

26
@GrimaceofDespair : 여기에 의견을 추가하겠습니다. 개인적으로 내부를 대형 다중 프로젝트 솔루션에서 "공용"수정 자의 매우 유용한 버전으로보고 있습니다. 이 수정자를 하위 프로젝트의 클래스에 추가하면 솔루션의 다른 하위 프로젝트가이 클래스를 "무작위로 사용"하는 것을 금지하고 내 라이브러리를 올바르게 사용할 수 있습니다. 나중에 리팩토링을해야 할 필요가 있다면, "자기 기다릴 필요가 없습니다. 그러나 왜 그 사람이 가서이 특정 계산 내에서 자신 만의 목적으로 만 필요한 Point 클래스의 인스턴스를 만들었습니까?"
KT.

3
다른 말로하면, 모든 사람들이 SmtpClient의 내부에 의존하고 어느 시점에서 버그가 발견되면, .NET은 버그를 고치기위한 심각한 문제가있을 수 있으며, 심지어 오랫동안 버그를 유지할 수도 있습니다. 다양한 내부 기능과 버그를 사용하는 모든 이전 프로그램과 "버그 호환성"을 유지하기 위해 Windows 개발자가 가야 할 길이를 설명한 재미있는 기사를 한 번 읽은 것을 기억합니다. .NET에서이 오류를 반복하는 것은 의미가 없습니다.
KT.

13
내부는 대중보다 WAY가 더 유용하다고 생각합니다. public을 사용하는 유일한 경우는 "여기, 사용자-사용하기위한 유용한 기능을 제공하고 있습니다."라고 명시 적으로 말할 때입니다. 사용자 라이브러리가 아닌 응용 프로그램을 작성하는 경우 외부에 호출 할 함수를 전혀 제공하지 않기 때문에 모든 것이 내부에 있어야합니다. 사용자 라이브러리를 작성하는 경우 시간이 끝날 때까지 사용자에게 제공, 유지 관리, 지원 및 유지하는 데 필요한 유용한 기능 만 공개됩니다. 그렇지 않으면 내부로 만드십시오.
Darrell Plank

85

Bob이 BigImportantClass를 필요로하는 경우 Bob은 프로젝트 A를 소유 한 사람들이 가입하여 BigImportantClass가 자신의 요구를 충족하도록 작성되고, 그의 요구를 충족하는지 테스트하고, 그의 요구를 충족시키는 것으로 문서화되고, 프로세스임을 확인하도록해야합니다. 더 이상 그의 필요를 충족시키기 위해 변경되지 않도록하기 위해

클래스가 내부 클래스 인 경우 해당 프로세스를 거치지 않아도되므로 프로젝트 A의 예산을 절약하여 다른 것에 소비 할 수 있습니다.

내부의 요점은 그것이 밥에게 삶을 어렵게한다는 것이 아닙니다. 프로젝트 A가 기능, 수명, 호환성 등에 대해 비싼 약속을 제어 할 수 있습니다.


5
완벽하게 이해됩니다. 우리는 여기에 모두 동의하는 성인이기 때문에 내부 회원을 무시할 수 있기를 바랍니다.
cwallenpoole 2013 년

6
@EricLippert 내가 의도 한 바가 아닙니다. "내부"가 포함 된 컴파일 된 코드를 상속 받았다면 멈췄습니까? 리플렉션을 사용하지 않는 한 단순히 컴파일러에게 "아니오, 다른 개발자가 당신에게 거짓말을했습니다. 대신 나를 믿어야합니다"라고 말할 방법이 없습니다.
cwallenpoole 2014 년

11
@cwallenpoole : 마음에 들지 않는 코드를 작성하는 거짓말 쟁이가 작성한 코드를 사용하는 경우 코드 사용을 중단하고 자신이 좋아하는 코드를 작성하십시오.
Eric Lippert

2
@EricLippert 그것은 뺨에 혀로되어 있었지만, 기분 나쁘다는 의미는 아니 었습니다. (주로 나는 그러한 경우에 SOL임을 확인하려고 노력했습니다).
cwallenpoole

5
@cwallenpoole : 나는 적어도 기분 나쁘지 않다. 불행한 상황에 처하게된다면 좋은 조언을 드리고 있습니다.
Eric Lippert

60

internal을 사용해야하는 또 다른 이유는 바이너리를 난독 처리하는 것입니다. obfuscator는 내부 클래스의 클래스 이름을 스크램블하는 것이 안전하지만 공개 클래스의 이름은 기존 참조를 손상시킬 수 있으므로 스크램블 할 수 없다는 것을 알고 있습니다.


4
Pff. 디스어셈블러로 바이트 코드를 보면 어쨌든 코드가 무엇을하는지 분명하게 알 수 있습니다. Obfuscators는 실제로 내부에 침입하려는 모든 사람에게 가벼운 억제책입니다. 유용한 기능 이름을 얻지 못했기 때문에 중지 한 해커에 대해 들어 본 적이 없습니다.
Nyerguds

28
잠이 들었을 때 문을 잠그지 마십시오. 자물쇠로 멈춘 도둑 소리를 들어 본 적이 없습니까?
Sergio

4
이것이 소스 코드에서 클래스와 변수 이름을 스크램블하는 이유입니다. PumpStateComparator가 무엇인지 알아낼 수는 있지만 LpWj4mWE가 무엇인지 추측 할 수 있습니까? #jobsecurity (내가하지 말고 실제로 인터넷에 접속하지 마십시오.)
Slothario

32

많은 복잡한 기능을 간단한 공개 API로 캡슐화하는 DLL을 작성하는 경우 공개적으로 노출되지 않는 클래스 멤버에는 "내부"가 사용됩니다.

복잡성 숨기기 (일명 캡슐화)는 품질 소프트웨어 엔지니어링의 주요 개념입니다.


20

내부 키워드는 관리되지 않는 코드 위에 래퍼를 작성할 때 많이 사용됩니다.

DllImport하려는 C / C ++ 기반 라이브러리가있는 경우 이러한 함수를 클래스의 정적 함수로 가져 와서 내부적으로 만들 수 있으므로 사용자는 원래 API가 아닌 랩퍼에만 액세스 할 수 있으므로 아무것도 엉망. 정적 함수는 필요한 여러 래퍼 클래스에 대해 어셈블리 어디에서나 사용할 수 있습니다.

Mono.Cairo를 살펴볼 수 있습니다.이 접근 방식을 사용하는 cairo 라이브러리를 감싸는 래퍼입니다.


12

"엄격한 수정 자로 사용"규칙에 따라 구동되므로 다른 어셈블리에서 명시 적으로 액세스해야 할 때까지 다른 클래스의 메소드에 액세스해야하는 모든 곳에서 내부를 사용합니다.

어셈블리 인터페이스는 일반적으로 클래스 인터페이스의 합보다 좁기 때문에 사용하는 곳이 많이 있습니다.


3
"엄격한 수정 자로 사용할 수있는"이유는 무엇입니까?
R. Martinho Fernandes

6
클래스 속성 / 메소드가 클래스 외부에서 액세스되어야하는 경우 private은 너무 엄격하고 다른 어셈블리에서 액세스해야하는 경우는 그리 많지 않습니다.
Ilya Komakhin 2009

11

내부가 많이 남용되는 것을 알았습니다. 당신은 실제로 다른 소비자에게 제공하지 않을 특정 클래스에만 특정 기능을 노출해서는 안됩니다.

제 생각에는 인터페이스가 깨지고 추상화가 깨집니다. 이것은 절대 사용해서는 안된다는 것이 아니라 더 나은 해결책은 다른 클래스로 리팩토링하거나 가능한 경우 다른 방식으로 사용하는 것입니다. 그러나 이것이 항상 가능한 것은 아닙니다.

문제가 발생할 수있는 이유는 다른 개발자가 귀하와 동일한 어셈블리에 다른 클래스를 작성해야 할 책임이 있기 때문입니다. 내부가 있으면 추상화의 선명도가 떨어지고 잘못 사용되면 문제가 발생할 수 있습니다. 공개 한 것과 같은 문제입니다. 다른 개발자가 작성하는 다른 클래스는 여전히 외부 클래스와 마찬가지로 소비자입니다. 클래스 추상화와 캡슐화는 외부 클래스를 보호하기위한 것이 아니라 모든 클래스를 보호하기위한 것입니다.

또 다른 문제는 많은 개발자들이 당시 어셈블리가 필요하지 않더라도 어셈블리의 다른 곳에서 사용하고 내부로 표시해야 할 수도 있다고 생각 한다는 것 입니다. 그런 다음 다른 개발자가 가지고 갈 것이라고 생각할 수 있습니다. 일반적으로 결정이 필요할 때까지 비공개로 표시하려고합니다.

그러나이 중 일부는 주관적 일 수 있으며 절대 사용해서는 안된다는 말은 아닙니다. 필요할 때만 사용하십시오.


종종 동일한 어셈블리 내에서 다른 도메인 개체에 공개해야하는 구성원이있는 도메인 개체를 작성합니다. 그러나 이러한 동일한 멤버를 어셈블리 외부의 클라이언트 코드에 사용할 수 없어야합니다. 이와 같은 상황에서는 '내부'가 매우 적합합니다.
록 앤서니 존슨

8

내가 기억할 수없는 블로그에서 다른 날, 아마 일주일 동안 흥미로운 것을 보았습니다. 기본적으로 나는 이것을 인정 할 수는 없지만 유용한 응용 프로그램이있을 것이라고 생각했습니다.

다른 어셈블리에서 추상 클래스를 볼 수 있지만 다른 클래스에서 상속받을 수는 없다고 가정하십시오. 봉인은 이유 때문에 추상적이기 때문에 작동하지 않습니다. 해당 어셈블리의 다른 클래스는 상속합니다. 다른 어셈블리의 어딘가에 Parent 클래스를 선언하려는 경우 Private이 작동하지 않습니다.

네임 스페이스 Base.Assembly
{
  공공 추상 클래스 부모
  {
    내부 추상 void SomeMethod ();
  }

  // 이것은 동일한 어셈블리에 있기 때문에 제대로 작동합니다.
  공개 클래스 ChildWithin : 부모
  {
    내부 재정의 void SomeMethod ()
    {
    }
  }
}

네임 스페이스 Another.Assembly
{
  // 내부 메소드를 재정의 할 수 없기 때문에 Kaaboom
  공개 클래스 ChildOutside : 부모
  {
  }

  공공 수업 시험 
  { 

    // 그냥 괜찮아
    개인 부모 _parent;

    공개 테스트 ()
    {
      // 아직 괜찮아
      _parent = new ChildWithin ();
    }
  }
}

보시다시피, 누군가 상속하지 않고도 Parent 클래스를 효과적으로 사용할 수 있습니다.


7

이 예에는 Assembly1.cs 및 Assembly2.cs라는 두 개의 파일이 있습니다. 첫 번째 파일에는 내부 기본 클래스 인 BaseClass가 있습니다. 두 번째 파일에서 BaseClass를 인스턴스화하려고하면 오류가 발생합니다.

// Assembly1.cs
// compile with: /target:library
internal class BaseClass 
{
   public static int intM = 0;
}

// Assembly1_a.cs
// compile with: /reference:Assembly1.dll
class TestAccess 
{
   static void Main()
   {  
      BaseClass myBase = new BaseClass();   // CS0122
   }
}

이 예제에서는 예제 1에서 사용한 것과 동일한 파일을 사용하고 BaseClass의 액세스 가능성 레벨을 public으로 변경하십시오 . 또한 멤버 IntM의 액세스 가능성 레벨을 internal로 변경하십시오 . 이 경우 클래스를 인스턴스화 할 수 있지만 내부 멤버에 액세스 할 수 없습니다.

// Assembly2.cs
// compile with: /target:library
public class BaseClass 
{
   internal static int intM = 0;
}

// Assembly2_a.cs
// compile with: /reference:Assembly1.dll
public class TestAccess 
{
   static void Main() 
   {      
      BaseClass myBase = new BaseClass();   // Ok.
      BaseClass.intM = 444;    // CS0117
   }
}

출처 : http://msdn.microsoft.com/en-us/library/7c5ka91b(VS.80).aspx


6

현재 어셈블리의 범위 내에서 액세스 할 수 있어야하며 메서드 외부에서 액세스 할 수없는 메서드, 클래스 등이있는 경우

예를 들어 DAL에 ORM이있을 수 있지만 개체는 비즈니스 계층에 노출되어서는 안되며 모든 상호 작용은 정적 메서드를 통해 수행하고 필요한 매개 변수를 전달해야합니다.


6

내부 멤버가 선언 된 어셈블리로만 제한되는 내부의 매우 흥미로운 사용은 어느 정도 "친구"기능을 얻는 것입니다. 친구 구성원은 선언 된 어셈블리 외부의 특정 다른 어셈블리에서만 볼 수있는 것입니다. C #은 친구를 기본적으로 지원하지 않지만 CLR은 지원합니다.

InternalsVisibleToAttribute 를 사용 하여 친구 어셈블리를 선언 할 수 있으며 친구 어셈블리 내의 모든 참조는 선언 어셈블리의 내부 구성원을 친구 어셈블리 범위 내에서 공용으로 취급합니다. 이것의 문제점은 모든 내부 구성원이 보인다는 것입니다. 당신은 선택하고 선택할 수 없습니다.

InternalsVisibleTo의 유용한 용도는 다양한 내부 멤버를 단위 테스트 어셈블리에 노출시켜 해당 멤버를 테스트하기 위해 복잡한 리플렉션 작업을 수행 할 필요가 없다는 것입니다. 보이는 모든 내부 멤버는 그다지 큰 문제는 아니지만이 방법을 사용하면 클래스 인터페이스가 상당히 무거워지고 선언 어셈블리 내에서 캡슐화를 망칠 수 있습니다.


5

일반적으로 두 가지 종류의 멤버가 있습니다.

  • public surface : 외부 어셈블리 (공개, 보호 및 내부 보호)에서 볼 수 있음 : 호출자를 신뢰할 수 없으므로 매개 변수 유효성 검사, 메서드 설명서 등이 필요합니다.
  • private surface : 외부 어셈블리 (비공개 및 내부 또는 내부 클래스)에서 볼 수 없음 : 호출자가 일반적으로 신뢰되므로 매개 변수 유효성 검사, 메서드 설명서 등이 생략 될 수 있습니다.

4

노이즈 감소, 노출이 적은 유형 일수록 라이브러리가 더 단순 해집니다. 변조 방지 / 보안은 또 다른 방법입니다 (반사가 그것에 대해 이길 수는 있지만).


4

내부 클래스를 사용하면 어셈블리의 API를 제한 할 수 있습니다. API를 이해하기 쉽게 만드는 것과 같은 이점이 있습니다.

또한 어셈블리에 버그가 있으면 수정 사항이 변경 될 가능성이 적습니다. 내부 클래스가 없으면 클래스의 공개 멤버를 변경하는 것이 주요 변경 사항이라고 가정해야합니다. 내부 클래스를 사용하면 공용 멤버를 수정하면 어셈블리 (및 InternalsVisibleTo 특성에서 참조되는 어셈블리)의 내부 API 만 중단한다고 가정 할 수 있습니다.

클래스 수준과 어셈블리 수준에서 캡슐화를 좋아합니다. 이에 동의하지 않는 사람들도 있지만, 기능을 사용할 수 있다는 것을 아는 것이 좋습니다.


2

데이터 백엔드에 LINQ-to-SQL을 사용하는 프로젝트가 있습니다. Biz와 Data라는 두 가지 주요 네임 스페이스가 있습니다. LINQ 데이터 모델은 데이터에 있으며 "내부"로 표시됩니다. Biz 네임 스페이스에는 LINQ 데이터 클래스를 둘러싼 공개 클래스가 있습니다.

따라서 Data.Client, Biz.Client; 후자는 데이터 객체의 모든 관련 속성을 노출합니다. 예 :

private Data.Client _client;
public int Id { get { return _client.Id; } set { _client.Id = value; } }

Biz 객체에는 팩토리 메소드를 강제로 사용하는 개인 생성자와 다음과 같은 내부 생성자가 있습니다.

internal Client(Data.Client client) {
    this._client = client;
}

라이브러리의 모든 비즈니스 클래스에서 사용할 수 있지만 프론트 엔드 (UI)는 데이터 모델에 직접 액세스 할 수 없으므로 비즈니스 계층이 항상 중개자 역할을합니다.

이것은 내가 실제로 internal많이 사용한 첫 번째이며, 매우 유용하다는 것을 증명합니다.


2

클래스 멤버를 만드는 것이 합리적 일 수 있습니다 internal. 클래스의 인스턴스화 방법을 제어하려는 경우를 예로들 수 있습니다. 클래스의 인스턴스를 만들기 위해 일종의 팩토리를 제공한다고 가정 해 봅시다. internal동일한 어셈블리에있는 팩토리가 클래스의 인스턴스를 만들 수는 있지만 해당 어셈블리 외부의 코드 는 만들 수 없도록 constructor 를 만들 수 있습니다.

그러나 클래스 나 멤버 internal를 특별한 이유없이 또는 클래스를 만드는 것이 의미가 없는 한 public또는 private특별한 이유없이 만드는 것은 아무 의미가 없습니다.



1

내부 키워드의 한 가지 사용은 어셈블리 사용자의 구체적인 구현에 대한 액세스를 제한하는 것입니다.

객체 생성을위한 팩토리 또는 다른 중앙 위치가있는 경우 어셈블리 사용자는 공용 인터페이스 또는 추상 기본 클래스 만 처리하면됩니다.

또한 내부 생성자를 사용하면 공개 클래스가 인스턴스화되는 위치와시기를 제어 할 수 있습니다.


1

어떻습니까? 일반적으로 List 개체를 어셈블리의 외부 사용자에게 노출시키지 말고 IEnumerable을 노출시키는 것이 좋습니다. 그러나 배열 구문과 다른 모든 List 메서드를 가져 오기 때문에 어셈블리 내에서 List 개체를 사용하는 것이 훨씬 쉽습니다. 따라서 일반적으로 어셈블리 내부에서 사용할 List를 노출하는 내부 속성이 있습니다.

이 접근법에 대한 의견을 환영합니다.


@Samik- "일반적으로 List 개체를 어셈블리의 외부 사용자에게 노출시키지 말고 IEnumerable을 노출시키는 것이 좋습니다." ienumerable이 선호되는 이유는 무엇입니까?
Howiecamp

1
@Howiecamp : 대부분 IEnumerable은 목록에 대한 읽기 전용 액세스를 제공하기 때문에 목록을 직접 노출하는 것처럼 의도하지 않은 클라이언트 (삭제 요소 등)에 의해 목록을 수정할 수 있습니다.
Samik R

또한 List 또는 IList를 노출하면 데이터에 대한 빠른 임의 액세스를 항상 허용하는 계약이 생성됩니다. 언젠가 다른 콜렉션을 사용하도록 메소드를 변경하거나 콜렉션을 전혀 사용하지 않는 경우 (수율 리턴) 값을 리턴하기 위해 목록을 작성하거나 메소드의 리턴 유형을 변경하여 계약을 중단해야합니다. 덜 구체적인 반환 유형을 사용하면 유연성이 제공됩니다.

1

public누군가 정의한 클래스 는 프로젝트 네임 스페이스를 볼 때 인텔리전스에 자동으로 표시됩니다. API 관점에서는 프로젝트 사용자에게 사용 가능한 클래스 만 표시하는 것이 중요합니다. 사용internal보이지 않는 것을 숨기려면 키워드를 하십시오.

귀하 Big_Important_Class의 프로젝트 A가 프로젝트 외부에서 사용되도록 의도 된 경우이를 표시해서는 안됩니다 internal.

그러나 많은 프로젝트에는 종종 프로젝트 내에서만 사용하도록 고안된 클래스가 있습니다. 예를 들어, 매개 변수화 된 스레드 호출에 대한 인수를 보유하는 클래스가있을 수 있습니다. 이러한 경우, internal의도하지 않은 API 변경으로부터 자신을 보호하는 것 외에 다른 이유가없는 것처럼 표시해야합니다 .


그렇다면 왜 private을 대신 사용하지 않습니까?
죄수 제로

1
private키워드는 다른 클래스 또는 구조체 내에서 정의 된 클래스 나 구조체에서 사용할 수 있습니다. 네임 스페이스 내에 정의 된 클래스는 public또는 만 선언 할 수 있습니다 internal.
Matt Davis

1

아이디어는 라이브러리를 디자인 할 때 (라이브러리의 클라이언트에 의해) 외부에서 사용하기위한 클래스 만 공개되어야한다는 것입니다. 이렇게하면 클래스를 숨길 수 있습니다.

  1. 향후 릴리스에서 변경 될 가능성이 높습니다 (공개 된 경우 클라이언트 코드가 손상 될 수 있음)
  2. 클라이언트에게 쓸모가 없으며 혼란을 일으킬 수 있습니다
  3. 안전하지 않습니다 (부적절하게 사용하면 라이브러리가 상당히 나빠질 수 있음)

기타

내부 요소를 사용하는 것보다 사내 솔루션을 개발하는 것이 중요하지 않은 것 같습니다. 일반적으로 클라이언트는 사용자와 지속적으로 연락하거나 코드에 액세스 할 수 있기 때문입니다. 그러나 라이브러리 개발자에게는 매우 중요합니다.


-7

객체 지향 패러다임에 잘 맞지 않는 클래스 또는 메소드가있는 경우 위험한 작업을 수행하고 제어하는 ​​다른 클래스 및 메소드에서 호출해야하며 다른 사람이 사용하지 못하게하려는 경우 .

public class DangerousClass {
    public void SafeMethod() { }
    internal void UpdateGlobalStateInSomeBizarreWay() { }
}

당신은 무엇을 말하려고하는? 명확하지 않습니다. 적어도 나 한테는 안돼
pqsk

@pqsk와 동의
Anynomous Khan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.