[C#] C # 인터페이스에 필드가 포함될 수없는 이유는 무엇입니까?

예를 들어 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 메소드가 포함될 수도 있습니다. 그러나 필드는 방법이 아닙니다 . 필드와 관련된 “슬롯”은 없으며 필드 위치를 참조하여 “채울 수”있습니다. 따라서 인터페이스는 메서드, 속성, 인덱서 및 이벤트를 정의 할 수 있지만 필드는 정의 할 수 없습니다.


답변

C #의 인터페이스는 특정 구현이 아니라 클래스가 준수 할 계약을 정의하기위한 것입니다.

그런 의미에서 C # 인터페이스 속성을 정의 할 수 있도록 합니다. 호출자는 다음에 대한 구현을 제공해야합니다.

interface ICar
{
    int Year { get; set; }
}

속성과 관련된 특별한 논리가없는 경우 클래스를 구현하면 자동 속성을 사용하여 구현을 단순화 할 수 있습니다.

class Automobile : ICar
{
    public int Year { get; set; } // automatically implemented
}


답변

속성으로 선언하십시오.

interface ICar {
   int Year { get; set; }
}


답변

에릭 리퍼 트 ​​(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; }
    }
}


답변

다른 사람들은 ‘왜’를 주었으므로 인터페이스가 컨트롤을 정의 할 수 있다고 덧붙일 것입니다. 속성으로 감싸면 :

public interface IView {
    Control Year { get; }
}


public Form : IView {
    public Control Year { get { return uxYear; } } //numeric text box or whatever
}