명시 적 인터페이스 구현을 사용하여 C #에서 멤버를 숨길 수 있습니까?


14

C #에서 인터페이스 및 명시 적 인터페이스 구현을 사용하는 방법을 알고 있지만 자주 사용하지 않는 특정 멤버를 숨기는 것이 나쁜 형식으로 간주되는지 궁금합니다. 예를 들면 다음과 같습니다.

public interface IMyInterface
{
    int SomeValue { get; set; }
    int AnotherValue { get; set; }
    bool SomeFlag { get; set; }

    event EventHandler SomeValueChanged;
    event EventHandler AnotherValueChanged;
    event EventHandler SomeFlagChanged;
}

유용한 이벤트는 거의 사용되지 않을 것이라는 것을 미리 알고 있습니다. 따라서 IntelliSense 혼란을 피하기 위해 명시 적 구현을 ​​통해 구현 클래스에서 숨기는 것이 합리적이라고 생각했습니다.


3
인터페이스를 통해 인스턴스를 참조하는 경우 어쨌든 숨기지 않으며 참조가 구체적 유형에 대한 경우에만 숨겨집니다.
Andy

1
나는 이것을 정기적으로 한 사람과 일했다. 그것은 나를 절대적으로 미치게 만들었습니다.
Joel Etherton

... 및 집계 사용을 시작하십시오.
mikalai

답변:


39

예, 일반적으로 나쁜 형태입니다.

따라서 IntelliSense 혼란을 피하기 위해 명시 적 구현을 ​​통해 구현 클래스에서 숨기는 것이 합리적이라고 생각했습니다.

IntelliSense가 너무 복잡하면 수업이 너무 큽니다. 수업이 너무 크면 너무 많은 일을하거나 설계가 잘못되었을 수 있습니다.

증상이 아닌 문제를 해결하십시오.


사용하지 않는 멤버에 대한 컴파일러 경고를 제거하는 데 사용하는 것이 적절합니까?
Gusdor

11
"증상이 아닌 문제를 해결하십시오." +1
FreeAsInBeer

11

의도 한 기능을 사용하십시오. 네임 스페이스 혼란을 줄이는 것은 그 중 하나가 아닙니다.

합법적 인 이유는 "숨김"이나 조직에 관한 것이 아니라 모호성을 해결하고 전문화해야합니다. "Foo"를 정의하는 두 개의 인터페이스를 구현하면 Foo의 의미가 다를 수 있습니다. 명시 적 인터페이스를 제외하고는 이것을 해결하는 더 좋은 방법은 아닙니다. 대안은 겹치는 인터페이스 구현을 허용하지 않거나 인터페이스가 시맨틱을 연관시킬 수 없음을 선언하는 것입니다 (어쨌든 개념적 일뿐입니다). 둘 다 나쁜 대안입니다. 때로는 실용성이 우아함을 능가합니다.

일부 작성자 / 전문가는이 기능을 기능의 핵심이라고 생각합니다 (메서드는 실제로 유형의 개체 모델의 일부가 아닙니다). 그러나 모든 기능과 마찬가지로 어딘가에 누군가가 필요합니다.

다시, 나는 그것을 숨기거나 조직하기 위해 사용 하지 않을 것 입니다. 인텔리전스에서 멤버를 숨기는 방법이 있습니다.

[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]

"저는 기능의 핵심이라고 생각하는 저명한 작가 / 전문가들이 있습니다." 누구입니까? 제안 된 대안은 무엇입니까? 명시 적 구현이없는 경우이를 해결하기 위해 C #에 다른 기능을 추가해야하는 하나 이상의 문제 (예 : 하위 인터페이스 / 구현 클래스의 인터페이스 메서드 특수화)가 있습니다 (ADO.NET 및 일반 컬렉션 참조). .
jhominal

@jhominal- Jeffrey Richter의 C #을 통한 CLR 은 특히 ​​kludge라는 단어를 사용합니다. C ++은 그것 없이도 잘 어울리므로 프로그래머는 명확하게 기본 캐스트를 참조하거나 캐스트를 사용해야합니다. 나는 대안이 그리 매력적이지 않다고 이미 언급했다. 그러나 OOP가 아닌 단일 상속 및 인터페이스의 측면.
codenheim

또한 동일한 "구현 + 인터페이스의 단일 상속"디자인에도 불구하고 Java에도 명시적인 구현이 없다고 언급 할 수도 있습니다. 그러나 Java에서는 재정의 메소드의 리턴 유형을 특수화 할 수 있으므로 명시적인 구현이 필요하지 않습니다. (C #에서는 속성 포함으로 인해 다른 디자인 결정이 내려졌습니다).
jhominal

@ jhominal-물론, 몇 분 후에 업데이트 할 것입니다. 감사.
codenheim

숨기기는 클래스가 인터페이스 계약을 준수하지만 유용 하지 않은 방식으로 인터페이스 메소드를 구현할 수있는 상황에서는 전적으로 적절할 수 있습니다 . 예를 들어, 나는 IDisposable.Dispose무언가를하지만 클래스 이름은 다른 클래스의 아이디어를 싫어 하지만 Close계약이 명시 Dispose적 인터페이스 구현을 사용 하지 않고 코드가 인스턴스를 생성하고 포기할 수 있다고 명시 적으로 부인하는 클래스에 대해서는 완벽하게 합리적이라고 생각합니다. IDisposable.Dispose아무것도하지 않는 방법을 정의하십시오 .
supercat

5

지저분한 코드의 징조 일 수도 있지만 실제 세계가 지저분한 경우가 있습니다. .NET 프레임 워크의 한 가지 예 는 변경 설정자 [], 추가 및 설정을 숨기는 ReadOnlyColection 입니다.

IE ReadOnlyCollection은 IList <T>를 구현합니다. 내가 이것을 숙고했을 때 몇 년 전에 SO에 대한 내 질문 을 참조하십시오 .


글쎄, 그것은 내가 사용할 인터페이스이기도하므로 처음부터 잘못하는 것은 의미가 없습니다.
Kyle Baran

2
나는 그것이 돌연변이 세터를 숨기지 않고 오히려 그것을 제공하지 않는다고 말하지 않을 것입니다. 더 좋은 예는 A) 소비자가 직접 호출하지 않고 B)가 절대적으로 필요한 경우 구현이 필요한 메소드를 사용할 수 있도록 명시 적으로 ConcurrentDictionary다양한 멤버를 구현 하는 것입니다 . 예를 들어, 사용자는 불필요한 예외를 피하기 보다는 전화 해야합니다 . IDictionary<T>ConcurrentDictionaryIDictionary<T>ConcurrentDictionary<T> TryAddAdd
Brian

물론 Add명시 적으로 구현 하면 혼란을 줄일 수 있습니다. TryAdd할 수있는 모든 작업을 수행 할 수 Add있습니다 ( Add에 대한 호출로 구현 TryAdd되므로 둘 다 중복되는 경우). 그러나 TryAdd이름 / 서명이 반드시 달라야 IDictionary하므로이 중복없이 구현할 수 없습니다. 명시 적 구현은이 문제를 깨끗하게 해결합니다.
Brian

소비자 관점에서 볼 때 방법은 숨겨져 있습니다.
Esben Skov Pedersen

1
IReadonlyCollection <T>에 대해서는 쓰지만 ReadOnlyCollection <T>에 링크하십시오. 인터페이스의 첫 번째는 두 번째 클래스입니다. ReadonlyCollection <T>가 수행하더라도 IReadonlyCollection <T>은 IList <T>를 구현하지 않습니다.
Jørgen Fogh 2016 년

2

구성원이 상황에 무의미 할 때 그렇게하는 것이 좋습니다. 예를 들어 IList<T>내부 객체에 위임하여 구현 되는 읽기 전용 컬렉션을 만들면 _wrapped다음과 같은 내용이있을 수 있습니다.

public T this[int index]
{
  get
  {
     return _wrapped[index];
  }
}
T IList<T>.this[int index]
{
  get
  {
    return this[index];
  }
  set
  {
    throw new NotSupportedException("Collection is read-only.");
  }
}
public int Count
{
  get { return _wrapped.Count; }
}
bool ICollection<T>.IsReadOnly
{
  get
  {
    return true;
  }
}

여기 네 가지 사례가 있습니다.

public T this[int index] 는 인터페이스가 아닌 클래스에 의해 정의되므로 물론 명시 적으로 구현되지는 않습니다.하지만 읽기-쓰기와 유사하다는 점에 유의하십시오. T this[int index] 인터페이스에 정의 된 하지만 읽기 전용입니다.

T IList<T>.this[int index]그중 일부 (게터)가 위의 속성과 완벽하게 일치하기 때문에 명시 적이며 다른 부분은 항상 예외를 throw합니다. 인터페이스를 통해이 클래스의 인스턴스에 액세스하는 사람에게는 매우 중요하지만 클래스 유형의 변수를 통해 해당 클래스를 사용하는 사람에게는 무의미합니다.

마찬가지로 bool ICollection<T>.IsReadOnly 항상 true를 반환 클래스 유형에 대해 작성된 코드에는 전혀 의미가 없지만 인터페이스 유형을 통해 코드를 사용하는 것이 중요 할 수 있으므로 명시 적으로 구현해야합니다.

거꾸로, public int Count 자체 유형을 통해 인스턴스를 사용하는 사람에게 잠재적으로 사용될 수 있으므로 명시 적으로 구현되지 않습니다.

그러나 "매우 드물게 사용 된"사례의 경우, 명시 적 구현을 ​​사용하지 않는 것이 좋습니다.

클래스 유형의 변수를 통해 메소드를 호출하는 명시 적 구현을 ​​사용하는 것이 권장되는 경우 오류 (인덱스 세터 사용 시도) 또는 무의미 (항상 동일한 값 확인)가 숨겨져 있습니다. 버그가 있거나 최적화되지 않은 코드로부터 사용자를 보호하고 있습니다. 그것은 거의 사용 되지 않을 것이라고 생각하는 코드와 크게 다릅니다 . 이를 위해 EditorBrowsable속성을 사용하여 멤버를 인텔리전스에서 숨기는 것을 고려할 수도 있습니다 . 사람들의 뇌에는 이미 관심없는 것을 걸러 내기위한 자체 소프트웨어가 있습니다.


인터페이스를 구현 하는 경우 인터페이스를 구현 하십시오. 그렇지 않으면 이는 Liskov 대체 원칙을 명백히 위반하는 것입니다.
Telastyn

@Telastyn 인터페이스가 실제로 구현되었습니다.
Jon Hanna

그러나 계약은 만족스럽지 못합니다. 특히 "이것은 변경 가능한 랜덤 액세스 콜렉션을 나타냅니다".
Telastyn

1
@Telastyn은 특히 "읽기 전용 인 컬렉션은 컬렉션을 만든 후 요소를 추가, 제거 또는 수정할 수 없습니다"라는 관점에서 문서화 된 계약을 이행합니다. msdn.microsoft.com/en-us/library/0cfatk9t%28v=vs.110%29.aspx
Jon Hanna

@Telastyn : 계약에 변경 가능성이 포함 된 경우 왜 인터페이스에 IsReadOnly속성이 포함 됩니까?
nikie
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.