인터페이스를 명시 적으로 구현하는 이유는 무엇입니까?


122

그렇다면 인터페이스를 명시 적으로 구현하는 좋은 사용 사례는 정확히 무엇일까요?

클래스를 사용하는 사람들이 intellisense에서 이러한 모든 메서드 / 속성을 볼 필요가 없도록하는 것입니까?

답변:


146

동일한 메서드와 다른 구현으로 두 개의 인터페이스를 구현하는 경우 명시 적으로 구현해야합니다.

public interface IDoItFast
{
    void Go();
}
public interface IDoItSlow
{
    void Go();
}
public class JustDoIt : IDoItFast, IDoItSlow
{
    void IDoItFast.Go()
    {
    }

    void IDoItSlow.Go()
    {
    }
}

네, 정확히 이것은 EIMI가 해결하는 한 가지 사례입니다. 그리고 다른 점은 "Michael B"답변으로 다룹니다.
crypted

10
훌륭한 예입니다. 인터페이스 / 클래스 이름을 좋아하십시오! :-)
Brian Rogers

11
나는 그것을 좋아하지 않는다. 매우 다른 일을하는 클래스에서 동일한 서명을 가진 두 개의 메서드? 이것은 매우 위험한 일이며 대규모 개발에 혼란을 일으킬 가능성이 매우 높습니다. 이와 같은 코드가 있으면 분석 및 디자인이 wahzoo라고 말할 것입니다.

4
@Mike 인터페이스는 일부 API 또는 두 개의 다른 API에 속할 수 있습니다. 여기에서는 사랑 이 약간 과장된 것일 수도 있지만 적어도 명시적인 구현이 가능하다는 사실에 기뻐할 것입니다.
TobiMcNamobi

@BrianRogers 그리고 너무 메소드 이름 ;-)
Sнаđошƒаӽ

66

선호하지 않는 회원을 숨기는 것이 유용합니다. 예를 들어 두 가지를 모두 구현 IComparable<T>하고 IComparable일반적 IComparable으로 다른 유형의 개체를 비교할 수 있다는 인상을주지 않도록 오버로드를 숨기는 것이 더 좋습니다. 마찬가지로 일부 인터페이스는과 같이 CLS 규격이 아니므로 인터페이스를 IConvertible명시 적으로 구현하지 않으면 CLS 규격이 필요한 언어의 최종 사용자가 개체를 사용할 수 없습니다. (BCL 구현자가 기본 요소의 IConvertible 멤버를 숨기지 않으면 매우 비참 할 것입니다. :))

또 다른 흥미로운 점은 일반적으로 그러한 구조를 사용한다는 것은 인터페이스를 명시 적으로 구현하는 구조체가 인터페이스 유형에 대한 boxing을 통해서만 호출 할 수 있다는 것을 의미합니다. 일반 제약 조건을 사용하여이 문제를 해결할 수 있습니다.

void SomeMethod<T>(T obj) where T:IConvertible

당신이 그것을 전달할 때 int를 boxing하지 않습니다.


1
제약 조건에 오타가 있습니다. 명확하게하기 위해 위의 코드가 작동합니다. 인터페이스에서 메서드 시그니처의 초기 선언에 있어야합니다. 원래 게시물은 이것을 지정하지 않았습니다. 또한 적절한 형식은 "void SomeMehtod <T> (T obj) 여기서 T : IConvertible입니다.") "와"where "사이에 존재하지 않아야하는 추가 콜론이 있습니다. 여전히 영리한 사용을 위해 +1 제네릭 비싼 권투를 방지 할 수 있습니다.
잭 Jannsen

1
안녕하세요 Michael B. 그래서 .NET에서 문자열을 구현할 때 IComparable의 공개 구현이있는 이유 : public int CompareTo (Object value) {if (value == null) {return 1; } if (! (value is String)) {throw new ArgumentException (Environment.GetResourceString ( "Arg_MustBeString")); } return String.Compare (this, (String) value, StringComparison.CurrentCulture); } 감사!
zzfima

string제네릭 이전에 주변에 있었고이 관행은 유행했습니다. .net 2가 등장했을 때 그들은의 공개 인터페이스를 깨고 싶지 않았기 string때문에 안전 장치가있는 그대로 두었습니다.
Michael B

37

인터페이스를 명시 적으로 구현해야하는 몇 가지 추가 이유 :

이전 버전과의 호환성 : ICloneable인터페이스가 변경되는 경우 구현하는 메서드 클래스 멤버는 메서드 서명을 변경할 필요가 없습니다.

클리너 코드 : CloneICloneable에서 메서드가 제거 되면 컴파일러 오류가 발생 하지만 메서드를 암시 적으로 구현하면 사용하지 않는 '고아'공용 메서드로 끝날 수 있습니다.

강력한 타이핑 : 예제를 통해 supercat의 이야기를 설명하기 위해 이것이 제가 선호하는 샘플 코드입니다. ICloneable명시 적으로 구현 Clone()하면 MyObject인스턴스 멤버 로 직접 호출 할 때 강력한 타이핑이 허용 됩니다 .

public class MyObject : ICloneable
{
  public MyObject Clone()
  {
    // my cloning logic;  
  }

  object ICloneable.Clone()
  {
    return this.Clone();
  }
}

이를 위해서는 interface ICloneable<out T> { T Clone(); T self {get;} }. ICloneable<T>T 에는 의도적으로 제약 이 없습니다 . 객체는 일반적으로베이스가 될 수있는 경우에만 안전하게 복제 할 수 있지만, 그렇게 할 수없는 클래스의 객체를 안전하게 복제 할 수있는베이스 클래스에서 파생 할 수 있습니다. 이를 허용하기 위해 상속 가능한 클래스에서 공용 복제 메서드를 노출하지 않는 것이 좋습니다. 대신 protected복제 메서드 가있는 상속 가능한 클래스와 그로부터 파생되고 공개 복제를 노출하는 봉인 된 클래스가 있습니다.
supercat

BCL에 ICloneable의 공변 버전이 없다는 점을 제외하면 더 좋을 것입니다. 그래서 하나를 만들어야합니다.
Wiebe Tijsma 2013 년

세 가지 예는 모두 예상치 못한 상황과 중단 인터페이스 모범 사례에 따라 달라집니다.
MickyD

13

또 다른 유용한 기술은 함수의 공용 메서드 구현이 인터페이스에 지정된 것보다 더 구체적인 값을 반환하도록하는 것입니다.

예를 들어 객체는을 구현할 수 ICloneable있지만 공개적으로 표시되는 Clone메서드가 자체 유형을 반환하도록합니다.

마찬가지로,는 IAutomobileFactory있을 수 있습니다 Manufacture을 반환하는 방법을 Automobile하지만 FordExplorerFactory, 어떤 구현 IAutomobileFactory, 그것있을 수 있습니다 Manufacture방법은 반환 FordExplorer(에서 어떤 도출을 Automobile). 타입 캐스트 할 필요없이 a에 의해 반환 된 객체에 대한 특정 속성을 FordExplorerFactory사용할 수 있음을 알고 있는 코드는 단지 어떤 유형이 있다는 것을 알고있는 코드 는 단순히 .FordExplorerFordExplorerFactoryIAutomobileFactoryAutomobile


2
+1 ...이, 명시 적 인터페이스 구현의 내 기본 설정 사용이 될 것입니다 비록 작은 코드 샘플 :이 이야기보다 약간 더 명확 아마 것
위브 Tijsma

7

멤버 이름과 서명이 동일한 두 개의 인터페이스가 있지만 사용 방법에 따라 동작을 변경하려는 경우에도 유용합니다. (나는 이와 같은 코드를 작성하지 않는 것이 좋습니다) :

interface Cat
{
    string Name {get;}
}

interface Dog
{
    string Name{get;}
}

public class Animal : Cat, Dog
{
    string Cat.Name
    {
        get
        {
            return "Cat";
        }
    }

    string Dog.Name
    {
        get
        {
            return "Dog";
        }
    }
}
static void Main(string[] args)
{
    Animal animal = new Animal();
    Cat cat = animal; //Note the use of the same instance of Animal. All we are doing is picking which interface implementation we want to use.
    Dog dog = animal;
    Console.WriteLine(cat.Name); //Prints Cat
    Console.WriteLine(dog.Name); //Prints Dog
}

60
내가 본 이상한 OO 관련 예 :public class Animal : Cat, Dog
mbx

38
@mbx : Animal도 Parrot을 구현했다면 Polly-morphing 동물이됩니다.
RenniePet 2013 년

3
나는 ;-) 다른 쪽 끝에서 한쪽 끝에서 고양이와 개이었다 만화 캐릭터 기억
조지 Birbilis

2
'80 년대에 TV 시리즈 ... "Manimal".. 남자가로 변신 할 수 있었다 .. 아, 신경 끄시 고
bkwdesign

Morkies는 고양이처럼 보이며 닌자 고양이도 마찬가지입니다.
samis

6

인터페이스를 명시 적으로 구현하기 위해 공용 인터페이스를 깔끔하게 유지할 수 있습니다. 즉, File클래스가 IDisposable명시 적으로 구현 하고 ) Close()보다 소비자에게 더 의미가 있는 공용 메서드 를 제공 할 수 있습니다 Dispose(.

F # 은 명시 적 인터페이스 구현 제공하므로 해당 기능에 액세스하려면 항상 특정 인터페이스로 캐스팅해야하므로 인터페이스를 매우 명시 적으로 (말장난없이) 사용할 수 있습니다.


대부분의 VB 버전은 명시 적 인터페이스 정의 만 지원한다고 생각합니다.
Gabe

1
@Gabe-VB의 경우보다 더 미묘합니다. 인터페이스를 구현하는 멤버의 이름 지정 및 액세스 가능성은 구현의 일부임을 나타내는 것과 별개입니다. 따라서 VB에서 @Iain의 답변 (현재 최고 답변)을 보면 각각 공개 멤버 "GoFast"및 "GoSlow"와 함께 IDoItFast 및 IDoItSlow를 구현할 수 있습니다.
Damien_The_Unbeliever

2
나는 당신의 특별한 예가 마음에 들지 않습니다 Dispose. 더 나은 예는 불변 컬렉션의 IList<T>.Add.
supercat

5

내부 인터페이스가 있고 클래스에서 멤버를 공개적으로 구현하지 않으려면 명시 적으로 구현합니다. 암시 적 구현은 공개되어야합니다.


좋습니다. 프로젝트가 암시 적 구현으로 컴파일되지 않는 이유를 설명합니다.
pauloya

4

명시 적 구현의 또 다른 이유는 유지 보수 가능성 입니다.

클래스가 "바쁜"상태가되었을 때 (예, 우리 모두가 다른 팀 구성원의 코드를 리팩토링 할 수있는 사치가 없습니다.) 명시 적으로 구현하면 인터페이스 계약을 충족 할 수있는 메서드가 있음을 분명히 알 수 있습니다.

따라서 코드의 "가독성"이 향상됩니다.


IMHO 클래스가 클라이언트에게 메소드를 노출해야하는지 여부를 결정하는 것이 더 중요합니다. 그것은 명시 적이든 암시 적이든 결정합니다. 여러 메서드가 함께 속한다는 것을 문서화하기 위해,이 경우에는 계약을 충족하기 때문에 #region적절한 제목 문자열을 사용합니다. 그리고 방법에 대한 의견.
ToolmakerSteve

1

System.Collections.Immutable작성자는 새로운 유형에 대해 의미가없는 인터페이스 부분을 긁어 내면서 컬렉션 유형에 대해 익숙한 API를 보존하기 위해이 기술을 사용하기로 선택한 다른 예를 에서 제공합니다.

구체적 ImmutableList<T>구현 IList<T>및 이에 따른 ICollection<T>( 순서는 있도록 ImmutableList<T>하면서도, 기존의 코드와 함께 쉽게 사용할 수) void ICollection<T>.Add(T item)대해 아무 의미 ImmutableList<T>기존 목록을 변경해서는 안 불변의리스트에 요소를 추가 버젼 : ImmutableList<T>또한 도출을으로부터 IImmutableList<T>IImmutableList<T> Add(T item)사용될 수있다 불변 목록.

따라서의 경우 Add구현은 ImmutableList<T>결국 다음과 같이 보입니다.

public ImmutableList<T> Add(T item)
{
    // Create a new list with the added item
}

IImmutableList<T> IImmutableList<T>.Add(T value) => this.Add(value);

void ICollection<T>.Add(T item) => throw new NotSupportedException();

int IList.Add(object value) => throw new NotSupportedException();

0

명시 적으로 정의 된 인터페이스의 경우 모든 메소드는 자동으로 비공개이므로 액세스 수정자를 공개 할 수 없습니다. 다음을 가정하십시오.

interface Iphone{

   void Money();

}

interface Ipen{

   void Price();
}


class Demo : Iphone, Ipen{

  void Iphone.Money(){    //it is private you can't give public               

      Console.WriteLine("You have no money");
  }

  void Ipen.Price(){    //it is private you can't give public

      Console.WriteLine("You have to paid 3$");
  }

}


// So you have to cast to call the method


    class Program
    {
        static void Main(string[] args)
        {
            Demo d = new Demo();

            Iphone i1 = (Iphone)d;

            i1.Money();

            ((Ipen)i1).Price();

            Console.ReadKey();
        }
    }

  // You can't call methods by direct class object

1
"모든 메소드는 자동으로 비공개입니다"-이것은 기술적으로 정확하지 않습니다. b / c가 실제로 비공개라면 캐스팅 여부에 관계없이 전혀 호출 할 수 없습니다.
samis

0

이것이 우리가 명시 적 인터페이스를 생성하는 방법입니다. 인터페이스 가 2 개이고 인터페이스가 동일한 메소드를 가지고 있고 단일 클래스가이 2 개의 인터페이스를 상속하는 경우, 하나의 인터페이스 메소드를 호출 할 때 컴파일러가 어떤 메소드를 호출할지 혼란스러워 할 수 있습니다. 명시 적 인터페이스를 사용하여이 문제를 관리합니다. 다음은 내가 아래에 준 한 가지 예입니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace oops3
{
    interface I5
    {
        void getdata();    
    }
    interface I6
    {
        void getdata();    
    }

    class MyClass:I5,I6
    {
        void I5.getdata()
        {
           Console.WriteLine("I5 getdata called");
        }
        void I6.getdata()
        {
            Console.WriteLine("I6 getdata called");
        }
        static void Main(string[] args)
        {
            MyClass obj = new MyClass();
            ((I5)obj).getdata();                     

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