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


223

예를 들어 ICar인터페이스를 원하고 모든 구현에 필드가 포함되어 있다고 가정 합니다 Year. 이것은 모든 구현이 별도로 선언 Year해야 한다는 것을 의미합니까 ? 인터페이스에서 이것을 간단히 정의하는 것이 좋지 않습니까?


21
인터페이스에는 구현이 없습니다.이를 위해 Year
PostMan

6
여기에 언급 된 내용에 추가하기 위해 인터페이스는 계약이며 필드는 값 (스칼라 또는 주소 포인터)을 넣을 수있는 시스템 메모리의 슬롯을 정의한다는 점에서 구현 세부 사항입니다.
herzmeister

5
그러나 현장이 공개라면 계약의 일부이며 구현 세부 사항뿐만 아니라 그렇지 않습니까?
Alex

답변:


238

다른 많은 답변이 의미 수준에서 정확하지만 구현 세부 정보 수준에서 이러한 종류의 질문에 접근하는 것이 흥미 롭습니다.

인터페이스는 메소드 를 포함 하는 슬롯 모음으로 생각할 수 있습니다 . 클래스가 인터페이스를 구현할 때 클래스는 런타임에 필요한 모든 슬롯을 채우는 방법을 알려 주어야합니다. 당신이 말할 때

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


26
필자가 때때로 놓친 한 가지는 인터페이스 수준 상수를 정의하는 Java와 유사한 기능으로, 언어에서 지원하기 위해 "슬롯"이 필요하지 않을 것입니다.
LBushkin

2
나는 간단한 말로 설명을 좋아한다. 감사. 자세한 내용은 "C #을 통한 CLR"및 "필수 .net 볼륨 1"을 제공합니다.
Sandeep GB

6
필드에 왜 슬롯이 없습니까? 연산자와 같은 질문입니까? 클래스가 인터페이스를 상속하지 않더라도 인터페이스가 구현되었는지 확인하기 위해 리플렉션을 사용하여 오리 타이핑에 대해 들었습니다. 필드를 끌어 올리기 위해 왜 반사 (또는 슬롯)를 사용할 수 없습니까? 나는 여전히 내 코드를 작성하고 있으므로 필드가 필요하지 않을 수도 있지만 연산자를 사용할 수 없다는 것을 알고 놀랐습니다. 운영자는 정확히 모든 오버로드 할 수 없습니다 제외하고 나의 이해의 방법과 같다 ( An interface cannot contain constants, fields, operators에서. msdn.microsoft.com/en-us/library/ms173156.aspx )

@acidzombie : 인터페이스가 연산자를 정의 할 수없는 이유에 대한 질문은 필드를 포함 할 수없는 이유와 다릅니다 (아마도 관련이 있음). 그래도 관심이 있다면 다른 질문을 게시 해 보시기 바랍니다.
Adam Robinson

1
@ b1nary.atr0phy 왜 필드를 사용합니까? 예를 들어 method를 선언 int One()하면 구현 public int One(){return 1;}이 필드가 아닙니다.
Hi-Angel

131

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

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

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

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

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

5
공개 된 모든 것이 계약의 일부는 아닙니다. 클래스에 공개 int Year가있는 경우 클래스 계약에 Year 유형의 필드가 있고 액세스 할 수 있다고 말하지 않습니까?
Didier A.

1
파티에 늦었지만 아닙니다.이 경우 계약에 속성 등급이 있으며 모든 클래스가 구현해야합니다. 속성은 실제로 get / set 메소드이며, 특별한 로직이 필요하지 않은 경우 자동으로 백업 필드가 생성됩니다. 특별한 구문은 더 명확한 표기법입니다.
user3613916

자동 구현에 상수 기본값 (예 : 123)을 어떻게 정의 할 수 Year있습니까?
lama12345

1
@ lama12345 나는 또한 파티에 늦었지만 C # 6 (2015, .NET Framework 4.6 및 .NET Core)부터 그 목적을 위해 자동 속성을 사용할 수 있습니다. public int Year => 123;. 그러나이 경우 setter를 갖는 것은 의미가 없으므로 인터페이스를 다음과 같이 정의해야합니다.int Year { get; }
EriF89

56

속성으로 선언하십시오.

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

47
문제는 " C # 인터페이스에 필드를 포함 할 수없는 이유무엇 입니까?"입니다. 이것은 그것을 다루지 않습니다.
AakashM

14
이 답변이 OP 질문에 대답하지는 않지만 내 문제를 해결한다는 데 동의합니다.
Gorgen

36

에릭 리퍼 트 ​​(Eric Lippert)가 못을 박았다. 인터페이스의 모든 멤버는 가상이며 인터페이스를 상속하는 클래스로 대체해야합니다. 인터페이스 선언에 virtual 키워드를 명시 적으로 쓰거나 클래스에서 override 키워드를 사용하지 마십시오.

virtual 키워드는 .NET에서 메소드와 메소드 포인터 배열 인 v-table로 구현됩니다. override 키워드는 v-table 슬롯을 다른 메소드 포인터로 채우고 기본 클래스에서 생성 된 것을 덮어 씁니다. 속성, 이벤트 및 인덱서는 기본적으로 메소드로 구현됩니다. 그러나 필드는 그렇지 않습니다. 따라서 인터페이스는 필드를 포함 할 수 없습니다.


Eric이 언급 한 슬롯의 개념에 기술적 / 실제 이름 (v-table)을 제공했습니다. 자세한 Hans 감사합니다.
RBT

인터페이스에서 기본 구현이 허용되지 않는 경우 v-table의 요점은 무엇입니까? 이것은 C # 8.0에서 변경되었지만 요점 옆에 있습니다.
AnthonyMonterrosa

19

Year완벽하게 괜찮은 속성을 가지고 있지 않습니까?

필드는 특정 데이터 표현 구현을 나타 내기 때문에 필드를 포함하지 않으며, 노출하면 캡슐화가 중단됩니다. 따라서 필드와의 인터페이스를 갖는 것은 인터페이스 대신 구현에 효과적으로 코딩하는 것인데, 이는 인터페이스가 갖는 흥미로운 역설입니다!

예를 들어, Year스펙의 일부는 ICar구현자가 Year현재 연도 + 1보다 늦거나 1900 년 이전에 할당을 허용 하는 것이 유효하지 않을 수 있습니다. Year필드를 노출 한 경우 사용하는 것이 훨씬 더 좋습니다 대신 작업을 수행하십시오.


18

짧은 대답은 그렇습니다. 모든 구현 유형은 자체 백업 변수를 만들어야합니다. 인터페이스가 계약과 유사하기 때문입니다. 구현 유형이 사용 가능하게해야하는 공개적으로 액세스 가능한 특정 코드를 지정하기 만하면됩니다. 코드 자체를 포함 할 수 없습니다.

제안하는 것을 사용하여이 시나리오를 고려하십시오.

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; }
    }
}

7

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

public interface IView {
    Control Year { get; }
}


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

3

인터페이스에는 구현이 없습니다.

  1. 속성이있는 인터페이스를 정의하십시오.
  2. 또한 모든 클래스에서 해당 인터페이스를 구현하고이 클래스를 계속 사용할 수 있습니다.
  3. 필요한 경우 클래스에서이 특성을 가상으로 정의하여 동작을 수정할 수 있습니다.

3

이미 많은 말을했지만 간단하게하기 위해 다음과 같이하겠습니다. 인터페이스는 소비자 또는 클래스가 메소드 계약을 구현하고 값을 저장하는 필드를 가지지 않습니다.

그렇다면 왜 속성이 허용 되는가? 따라서 간단한 대답은-속성은 내부적으로 메소드로만 정의됩니다.


1
회원에게 액세스해야하는 경우 속성으로 지정하면됩니다.
Unome

0

이를 위해 연도 필드를 구현하는 Car 기본 클래스를 가질 수 있으며 다른 모든 구현은 상속을받을 수 있습니다.


0

인터페이스는 퍼블릭 인스턴스 속성 및 메서드를 정의 합니다. 필드는 일반적으로 개인용이거나 가장 보호 된 내부 또는 보호 된 내부 (일반적으로 "필드"라는 용어는 공용으로 사용되지 않습니다)입니다.

다른 답변에서 언급했듯이 기본 클래스를 정의하고 모든 상속자가 액세스 할 수있는 보호 된 속성을 정의 할 수 있습니다.

한 가지 이상한 점은 인터페이스를 실제로 내부 로 정의 할 수 있지만 인터페이스의 유용성을 제한하며 일반적으로 다른 외부 코드에서 사용하지 않는 내부 기능을 정의하는 데 사용됩니다.

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