例如,假设我想要一个ICar
接口,并且所有实现都包含字段Year
。这是否意味着每个实现都必须单独声明Year
?简单地在界面中定义它会不会更好?
答案 0 :(得分:226)
虽然许多其他答案在语义层面上是正确的,但我发现从实现细节层面处理这些问题也很有意思。
接口可以被视为 slots 的集合,其中包含 methods 。当类实现接口时,该类需要告诉运行时如何填充所有必需的槽。当你说
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方法。但字段不是方法。没有与某个字段相关联的“插槽”,您可以随后通过对字段位置的引用来“填充”该字段。因此,接口可以定义方法,属性,索引器和事件,但不能定义字段。
答案 1 :(得分:126)
C#中的接口旨在定义类将遵守的合同 - 而不是特定的实现。
本着这种精神,C#接口允许定义属性 - 调用者必须为其提供实现:
interface ICar
{
int Year { get; set; }
}
如果没有与属性关联的特殊逻辑,则实现类可以使用自动属性来简化实现:
class Automobile : ICar
{
public int Year { get; set; } // automatically implemented
}
答案 2 :(得分:48)
将其声明为属性:
interface ICar {
int Year { get; set; }
}
答案 3 :(得分:35)
Eric Lippert钉了它,我会用不同的方式说出他说的话。接口的所有成员都是虚拟的,它们都需要被继承接口的类覆盖。您没有在接口声明中显式编写virtual关键字,也没有在类中使用override关键字,这些都是隐含的。
虚拟关键字在.NET中使用方法和所谓的v-table(方法指针数组)实现。 override关键字用不同的方法指针填充v表槽,覆盖基类生成的那个。属性,事件和索引器作为方法实现。但是领域不是。因此,接口不能包含字段。
答案 4 :(得分:19)
为什么不只有一个Year
属性,这完全没问题?
接口不包含字段,因为字段表示数据表示的特定实现,并且公开它们会破坏封装。因此,具有字段的接口将有效地编码到实现而不是接口,这对于接口来说是一个奇怪的悖论!
例如,Year
规范的一部分可能要求ICar
实施者无效,以允许分配到Year
,该Year
晚于当前年份+ 1或1900年之前如果你暴露了{{1}}字段,那就没有办法说了 - 更好的是使用属性来代替这里的工作。
答案 5 :(得分:17)
简短的回答是肯定的,每个实现类型都必须创建自己的支持变量。这是因为界面类似于合同。它所能做的只是指定实现类型必须提供的特定公共可访问代码段;它本身不能包含任何代码。
使用您的建议考虑这种情况:
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; }
}
}
答案 6 :(得分:5)
其他人已经给出了'为什么'所以我只是添加你的界面可以定义一个控件;如果你将它包装在一个属性中:
public interface IView {
Control Year { get; }
}
public Form : IView {
public Control Year { get { return uxYear; } } //numeric text box or whatever
}
答案 7 :(得分:3)
接口不包含任何实现。
答案 8 :(得分:2)
已经说了很多,但为了简单起见,这是我的看法。 接口旨在使消费者或类实现方法契约,而不是具有存储值的字段。
您可能会争辩为什么允许属性?所以简单的答案是 - 属性在内部仅定义为方法。
答案 9 :(得分:0)
为此你可以有一个Car基类来实现year字段,所有其他实现都可以从它继承。
答案 10 :(得分:0)
界面定义 public 实例属性和方法。字段通常是私有的,或者受保护最严格的内部或受保护的内部字段(术语“字段”通常不用于任何公共字段)。
如其他回复所述,您可以定义基类并定义所有继承者都可以访问的受保护属性。
一个奇怪的是,接口实际上可以定义为 internal ,但它限制了接口的有用性,并且它通常用于定义其他外部代码未使用的内部功能。
答案 11 :(得分:0)
从C#8.0开始,接口可以为成员(包括属性)定义默认实现。为接口中的属性定义默认实现的情况很少见,因为接口可能未定义实例数据字段。
interface IEmployee
{
string Name
{
get;
set;
}
int Counter
{
get;
}
}
public class Employee : IEmployee
{
public static int numberOfEmployees;
private string _name;
public string Name // read-write instance property
{
get => _name;
set => _name = value;
}
private int _counter;
public int Counter // read-only instance property
{
get => _counter;
}
// constructor
public Employee() => _counter = ++numberOfEmployees;
}