답변:
동일한 메서드와 다른 구현으로 두 개의 인터페이스를 구현하는 경우 명시 적으로 구현해야합니다.
public interface IDoItFast
{
void Go();
}
public interface IDoItSlow
{
void Go();
}
public class JustDoIt : IDoItFast, IDoItSlow
{
void IDoItFast.Go()
{
}
void IDoItSlow.Go()
{
}
}
선호하지 않는 회원을 숨기는 것이 유용합니다. 예를 들어 두 가지를 모두 구현 IComparable<T>
하고 IComparable
일반적 IComparable
으로 다른 유형의 개체를 비교할 수 있다는 인상을주지 않도록 오버로드를 숨기는 것이 더 좋습니다. 마찬가지로 일부 인터페이스는과 같이 CLS 규격이 아니므로 인터페이스를 IConvertible
명시 적으로 구현하지 않으면 CLS 규격이 필요한 언어의 최종 사용자가 개체를 사용할 수 없습니다. (BCL 구현자가 기본 요소의 IConvertible 멤버를 숨기지 않으면 매우 비참 할 것입니다. :))
또 다른 흥미로운 점은 일반적으로 그러한 구조를 사용한다는 것은 인터페이스를 명시 적으로 구현하는 구조체가 인터페이스 유형에 대한 boxing을 통해서만 호출 할 수 있다는 것을 의미합니다. 일반 제약 조건을 사용하여이 문제를 해결할 수 있습니다.
void SomeMethod<T>(T obj) where T:IConvertible
당신이 그것을 전달할 때 int를 boxing하지 않습니다.
string
제네릭 이전에 주변에 있었고이 관행은 유행했습니다. .net 2가 등장했을 때 그들은의 공개 인터페이스를 깨고 싶지 않았기 string
때문에 안전 장치가있는 그대로 두었습니다.
인터페이스를 명시 적으로 구현해야하는 몇 가지 추가 이유 :
이전 버전과의 호환성 : ICloneable
인터페이스가 변경되는 경우 구현하는 메서드 클래스 멤버는 메서드 서명을 변경할 필요가 없습니다.
클리너 코드 : Clone
ICloneable에서 메서드가 제거 되면 컴파일러 오류가 발생 하지만 메서드를 암시 적으로 구현하면 사용하지 않는 '고아'공용 메서드로 끝날 수 있습니다.
강력한 타이핑 : 예제를 통해 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
복제 메서드 가있는 상속 가능한 클래스와 그로부터 파생되고 공개 복제를 노출하는 봉인 된 클래스가 있습니다.
또 다른 유용한 기술은 함수의 공용 메서드 구현이 인터페이스에 지정된 것보다 더 구체적인 값을 반환하도록하는 것입니다.
예를 들어 객체는을 구현할 수 ICloneable
있지만 공개적으로 표시되는 Clone
메서드가 자체 유형을 반환하도록합니다.
마찬가지로,는 IAutomobileFactory
있을 수 있습니다 Manufacture
을 반환하는 방법을 Automobile
하지만 FordExplorerFactory
, 어떤 구현 IAutomobileFactory
, 그것있을 수 있습니다 Manufacture
방법은 반환 FordExplorer
(에서 어떤 도출을 Automobile
). 타입 캐스트 할 필요없이 a에 의해 반환 된 객체에 대한 특정 속성을 FordExplorerFactory
사용할 수 있음을 알고 있는 코드는 단지 어떤 유형이 있다는 것을 알고있는 코드 는 단순히 .FordExplorer
FordExplorerFactory
IAutomobileFactory
Automobile
멤버 이름과 서명이 동일한 두 개의 인터페이스가 있지만 사용 방법에 따라 동작을 변경하려는 경우에도 유용합니다. (나는 이와 같은 코드를 작성하지 않는 것이 좋습니다) :
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
}
public class Animal : Cat, Dog
인터페이스를 명시 적으로 구현하기 위해 공용 인터페이스를 깔끔하게 유지할 수 있습니다. 즉, File
클래스가 IDisposable
명시 적으로 구현 하고 ) Close()
보다 소비자에게 더 의미가 있는 공용 메서드 를 제공 할 수 있습니다 Dispose(
.
F # 은 명시 적 인터페이스 구현 만 제공하므로 해당 기능에 액세스하려면 항상 특정 인터페이스로 캐스팅해야하므로 인터페이스를 매우 명시 적으로 (말장난없이) 사용할 수 있습니다.
Dispose
. 더 나은 예는 불변 컬렉션의 IList<T>.Add
.
명시 적 구현의 또 다른 이유는 유지 보수 가능성 입니다.
클래스가 "바쁜"상태가되었을 때 (예, 우리 모두가 다른 팀 구성원의 코드를 리팩토링 할 수있는 사치가 없습니다.) 명시 적으로 구현하면 인터페이스 계약을 충족 할 수있는 메서드가 있음을 분명히 알 수 있습니다.
따라서 코드의 "가독성"이 향상됩니다.
#region
적절한 제목 문자열을 사용합니다. 그리고 방법에 대한 의견.
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();
명시 적으로 정의 된 인터페이스의 경우 모든 메소드는 자동으로 비공개이므로 액세스 수정자를 공개 할 수 없습니다. 다음을 가정하십시오.
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
이것이 우리가 명시 적 인터페이스를 생성하는 방법입니다. 인터페이스 가 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();
}
}
}