예를 들어 ICar
인터페이스를 원하고 모든 구현에 필드가 포함되어 있다고 가정 합니다 Year
. 이것은 모든 구현이 별도로 선언 Year
해야 한다는 것을 의미합니까 ? 인터페이스에서 이것을 간단히 정의하는 것이 좋지 않습니까?
예를 들어 ICar
인터페이스를 원하고 모든 구현에 필드가 포함되어 있다고 가정 합니다 Year
. 이것은 모든 구현이 별도로 선언 Year
해야 한다는 것을 의미합니까 ? 인터페이스에서 이것을 간단히 정의하는 것이 좋지 않습니까?
답변:
다른 많은 답변이 의미 수준에서 정확하지만 구현 세부 정보 수준에서 이러한 종류의 질문에 접근하는 것이 흥미 롭습니다.
인터페이스는 메소드 를 포함 하는 슬롯 모음으로 생각할 수 있습니다 . 클래스가 인터페이스를 구현할 때 클래스는 런타임에 필요한 모든 슬롯을 채우는 방법을 알려 주어야합니다. 당신이 말할 때
interface IFoo { void M(); }
class Foo : IFoo { public void M() { ... } }
"내 인스턴스를 만들 때 IFoo.M의 슬롯에 Foo.M에 대한 참조를 넣습니다.
그런 다음 전화를 걸 때 :
IFoo ifoo = new Foo();
ifoo.M();
컴파일러는 "IFoo.M의 슬롯에 어떤 메서드가 있는지 개체에게 물어보고 해당 메서드를 호출하는 코드를 생성합니다.
인터페이스가 메소드를 포함하는 슬롯의 콜렉션 인 경우 해당 슬롯 중 일부에는 특성의 get 및 set 메소드, 인덱서의 get 및 set 메소드 및 이벤트의 add 및 remove 메소드가 포함될 수도 있습니다. 그러나 필드는 방법이 아닙니다 . 필드와 관련된 "슬롯"은 없으며 필드 위치를 참조하여 "채울 수"있습니다. 따라서 인터페이스는 메서드, 속성, 인덱서 및 이벤트를 정의 할 수 있지만 필드는 정의 할 수 없습니다.
An interface cannot contain constants, fields, operators
에서. msdn.microsoft.com/en-us/library/ms173156.aspx )
int One()
하면 구현 public int One(){return 1;}
이 필드가 아닙니다.
C #의 인터페이스는 특정 구현이 아니라 클래스가 준수 할 계약을 정의하기위한 것입니다.
그런 의미에서 C # 인터페이스 는 속성을 정의 할 수 있도록 합니다. 호출자는 다음에 대한 구현을 제공해야합니다.
interface ICar
{
int Year { get; set; }
}
속성과 관련된 특별한 논리가없는 경우 클래스를 구현하면 자동 속성을 사용하여 구현을 단순화 할 수 있습니다.
class Automobile : ICar
{
public int Year { get; set; } // automatically implemented
}
Year
있습니까?
public int Year => 123;
. 그러나이 경우 setter를 갖는 것은 의미가 없으므로 인터페이스를 다음과 같이 정의해야합니다.int Year { get; }
에릭 리퍼 트 (Eric Lippert)가 못을 박았다. 인터페이스의 모든 멤버는 가상이며 인터페이스를 상속하는 클래스로 대체해야합니다. 인터페이스 선언에 virtual 키워드를 명시 적으로 쓰거나 클래스에서 override 키워드를 사용하지 마십시오.
virtual 키워드는 .NET에서 메소드와 메소드 포인터 배열 인 v-table로 구현됩니다. override 키워드는 v-table 슬롯을 다른 메소드 포인터로 채우고 기본 클래스에서 생성 된 것을 덮어 씁니다. 속성, 이벤트 및 인덱서는 기본적으로 메소드로 구현됩니다. 그러나 필드는 그렇지 않습니다. 따라서 인터페이스는 필드를 포함 할 수 없습니다.
왜 Year
완벽하게 괜찮은 속성을 가지고 있지 않습니까?
필드는 특정 데이터 표현 구현을 나타 내기 때문에 필드를 포함하지 않으며, 노출하면 캡슐화가 중단됩니다. 따라서 필드와의 인터페이스를 갖는 것은 인터페이스 대신 구현에 효과적으로 코딩하는 것인데, 이는 인터페이스가 갖는 흥미로운 역설입니다!
예를 들어, Year
스펙의 일부는 ICar
구현자가 Year
현재 연도 + 1보다 늦거나 1900 년 이전에 할당을 허용 하는 것이 유효하지 않을 수 있습니다. Year
필드를 노출 한 경우 사용하는 것이 훨씬 더 좋습니다 대신 작업을 수행하십시오.
짧은 대답은 그렇습니다. 모든 구현 유형은 자체 백업 변수를 만들어야합니다. 인터페이스가 계약과 유사하기 때문입니다. 구현 유형이 사용 가능하게해야하는 공개적으로 액세스 가능한 특정 코드를 지정하기 만하면됩니다. 코드 자체를 포함 할 수 없습니다.
제안하는 것을 사용하여이 시나리오를 고려하십시오.
public interface InterfaceOne
{
int myBackingVariable;
int MyProperty { get { return myBackingVariable; } }
}
public interface InterfaceTwo
{
int myBackingVariable;
int MyProperty { get { return myBackingVariable; } }
}
public class MyClass : InterfaceOne, InterfaceTwo { }
여기 몇 가지 문제가 있습니다.
myBackingVariable
것을 MyClass
사용할 것인가?가장 일반적인 방법은 인터페이스와 인터페이스를 구현하는 기본 추상 클래스를 선언하는 것입니다. 이를 통해 추상 클래스에서 상속하고 무료로 구현을 얻거나 인터페이스를 명시 적으로 구현하고 다른 클래스에서 상속 할 수있는 유연성이 있습니다. 다음과 같이 작동합니다.
public interface IMyInterface
{
int MyProperty { get; set; }
}
public abstract class MyInterfaceBase : IMyInterface
{
int myProperty;
public int MyProperty
{
get { return myProperty; }
set { myProperty = value; }
}
}
이미 많은 말을했지만 간단하게하기 위해 다음과 같이하겠습니다. 인터페이스는 소비자 또는 클래스가 메소드 계약을 구현하고 값을 저장하는 필드를 가지지 않습니다.
그렇다면 왜 속성이 허용 되는가? 따라서 간단한 대답은-속성은 내부적으로 메소드로만 정의됩니다.
이를 위해 연도 필드를 구현하는 Car 기본 클래스를 가질 수 있으며 다른 모든 구현은 상속을받을 수 있습니다.
인터페이스는 퍼블릭 인스턴스 속성 및 메서드를 정의 합니다. 필드는 일반적으로 개인용이거나 가장 보호 된 내부 또는 보호 된 내부 (일반적으로 "필드"라는 용어는 공용으로 사용되지 않습니다)입니다.
다른 답변에서 언급했듯이 기본 클래스를 정의하고 모든 상속자가 액세스 할 수있는 보호 된 속성을 정의 할 수 있습니다.
한 가지 이상한 점은 인터페이스를 실제로 내부 로 정의 할 수 있지만 인터페이스의 유용성을 제한하며 일반적으로 다른 외부 코드에서 사용하지 않는 내부 기능을 정의하는 데 사용됩니다.