为什么C#接口不能包含字段?

del*_*ber 217 c# interface

例如,假设我想要一个ICar接口,并且所有实现都将包含该字段Year.这是否意味着每个实现都必须单独声明Year?简单地在界面中定义它不是更好吗?

Eri*_*ert 230

虽然许多其他答案在语义层面上是正确的,但我发现从实现细节层面处理这些问题也很有趣.

接口可以被认为是包含方法插槽集合.当类实现接口时,该类需要告诉运行时如何填充所有必需的槽.当你说

interface IFoo { void M(); } 
class Foo : IFoo { public void M() { ... } }
Run Code Online (Sandbox Code Playgroud)

该类说"当你创建我的实例时,在IFoo.M的插槽中填写对Foo.M的引用.

然后当你打电话时:

IFoo ifoo = new Foo();
ifoo.M();
Run Code Online (Sandbox Code Playgroud)

编译器生成的代码"询问对象在IFoo.M的插槽中有什么方法,并调用该方法.

如果接口是包含方法的插槽集合,那么其中一些插槽还可以包含属性的get和set方法,索引器的get和set方法以及事件的add和remove方法.但是一个领域不是一种方法.没有与某个字段相关联的"插槽",您可以随后通过对字段位置的引用来"填充"该字段.因此,接口可以定义方法,属性,索引器和事件,但不能定义字段.

  • 我有时会想到的一件事是类似于java的能力来定义接口级常量,这可能不需要"槽"来支持该语言. (25认同)
  • 为什么一个字段没有插槽?和运营商一样的问题?我记得听说过使用反射进行鸭子打字,看看是否实现了接口,即使该类没有继承接口.为什么不能使用反射(或槽)来拉起场?我仍在编写我的代码所以我可能不需要/想要字段但我很惊讶地发现我不能使用运算符.运算符与我理解的方法完全相同,但不是所有都可以重载(`接口不能包含常量,字段,运算符`.来自http://msdn.microsoft.com/en-us/library/ms173156.aspx) (5认同)
  • 我喜欢用简单的词语解释.谢谢."CLR via C#"和"Essential .net volume 1"提供了更多详细信息. (2认同)

LBu*_*kin 130

C#中的接口旨在定义类将遵守的合同 - 而不是特定的实现.

本着这种精神,C#接口确实允许定义属性 - 调用者必须为以下内容提供实现:

interface ICar
{
    int Year { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

如果没有与属性关联的特殊逻辑,则实现类可以使用自动属性来简化实现:

class Automobile : ICar
{
    public int Year { get; set; } // automatically implemented
}
Run Code Online (Sandbox Code Playgroud)

  • 并非公开的一切都是合同的一部分.如果一个类有公共int年份,是不是说类合同有一个类型为Year的字段,并且可以访问? (4认同)
  • 迟到了,但没有,在这种情况下,这意味着合同有一个财产年,任何遵守的阶级都应该实施。属性实际上是 get/set 方法,如果不需要特殊逻辑,它们会自动生成支持字段。特殊语法只是为了更清晰的表示法。 (2认同)

Tar*_*don 51

将其声明为属性:

interface ICar {
   int Year { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

  • 问题是"**为什么**不能C#接口包含字段?".这没有解决这个问题. (45认同)
  • 我同意这个答案不回答OP问题,但是,它解决了我的问题. (10认同)

Han*_*ant 35

Eric Lippert钉了它,我会用不同的方式说出他说的话.接口的所有成员都是虚拟的,它们都需要被继承接口的类覆盖.您没有在接口声明中显式编写virtual关键字,也没有在类中使用override关键字,它们是隐含的.

virtual关键字在.NET中使用方法和所谓的v-table(方法指针数组)实现.override关键字用不同的方法指针填充v表槽,覆盖基类生成的那个.属性,事件和索引器作为方法实现.但是领域不是.因此,接口不能包含字段.


Joh*_*lla 19

为什么不拥有一个Year属性,这是完美的?

接口不包含字段,因为字段表示数据表示的特定实现,并且公开它们会破坏封装.因此,具有字段的接口将有效地编码到实现而不是接口,这对于接口来说是一个奇怪的悖论!

例如,您的Year规范的一部分可能要求ICar实施者无法分配到Year晚于当前年份+ 1或1900年之前的部分.如果您有暴露的Year字段,则无法说明- 更好用而不是在这里做工作.


Ada*_*son 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 { }
Run Code Online (Sandbox Code Playgroud)

我们在这里有几个问题:

  • 因为接口的所有成员 - 根据定义 - 是公共的,所以我们的支持变量现在暴露给使用该接口的任何人
  • 哪个myBackingVariableMyClass用?

最常用的方法是声明接口和实现它的准系统抽象类.这使您可以灵活地继承抽象类并免费获得实现,或者显式实现接口并允许从其他类继承.它的工作原理如下:

public interface IMyInterface
{
    int MyProperty { get; set; }
}

public abstract class MyInterfaceBase : IMyInterface
{
    int myProperty;

    public int MyProperty
    {
        get { return myProperty; }
        set { myProperty = value; }
    }
}
Run Code Online (Sandbox Code Playgroud)


zom*_*omf 7

其他人给出了"为什么",所以我只想补充说你的界面可以定义一个控件; 如果你将它包装在一个属性中:

public interface IView {
    Control Year { get; }
}


public Form : IView {
    public Control Year { get { return uxYear; } } //numeric text box or whatever
}
Run Code Online (Sandbox Code Playgroud)


Pra*_*thi 5

已经说了很多了,但为了简单起见,这是我的看法。接口旨在具有由消费者或类实现的方法契约,而不具有用于存储值的字段。

您可能会争论那为什么允许财产呢?所以简单的答案是 - 属性仅在内部定义为方法。