为什么 C# 接口不能包含字段?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2115114/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why can't C# interfaces contain fields?
提问by deltanovember
For example, suppose I want an ICar
interface and that all implementations will contain the field Year
. Does this mean that every implementation has to separately declare Year
? Wouldn't it be nicer to simply define this in the interface?
例如,假设我想要一个ICar
接口并且所有实现都将包含字段Year
。这是否意味着每个实现都必须单独声明Year
?简单地在接口中定义它不是更好吗?
采纳答案by Eric Lippert
Though many of the other answers are correct at the semantic level, I find it interesting to also approach these sorts of questions from the implementation details level.
尽管许多其他答案在语义级别上是正确的,但我发现从实现细节级别处理这些类型的问题也很有趣。
An interface can be thought of as a collection of slots, which contain methods. When a class implements an interface, the class is required to tell the runtime how to fill in all the required slots. When you say
接口可以被认为是包含方法的槽的集合。当一个类实现一个接口时,该类需要告诉运行时如何填充所有必需的槽。当你说
interface IFoo { void M(); }
class Foo : IFoo { public void M() { ... } }
the class says "when you create an instance of me, stuff a reference to Foo.M in the slot for IFoo.M.
该类说“当您创建 me 的实例时,在 IFoo.M 的插槽中填充对 Foo.M 的引用。
Then when you do a call:
然后当你打电话时:
IFoo ifoo = new Foo();
ifoo.M();
the compiler generates code that says "ask the object what method is in the slot for IFoo.M, and call that method.
编译器生成的代码表示“向对象询问 IFoo.M 的插槽中的方法是什么,然后调用该方法。
If an interface is a collection of slots that contain methods, then some of those slots can also contain the get and set methods of a property, the get and set methods of an indexer, and the add and remove methods of an event. But a field is not a method. There's no "slot" associated with a field that you can then "fill in" with a reference to the field location. And therefore, interfaces can define methods, properties, indexers and events, but not fields.
如果接口是包含方法的槽的集合,那么其中一些槽还可以包含属性的 get 和 set 方法、索引器的 get 和 set 方法以及事件的 add 和 remove 方法。但是字段不是方法。没有与字段相关联的“槽”,然后您可以使用对字段位置的引用来“填充”。因此,接口可以定义方法、属性、索引器和事件,但不能定义字段。
回答by Tarydon
Declare it as a property:
将其声明为属性:
interface ICar {
int Year { get; set; }
}
回答by John Feminella
Why not just have a Year
property, which is perfectly fine?
为什么不只是拥有一个Year
完全没问题的财产?
Interfaces don't contain fields because fields represent a specific implementation of data representation, and exposing them would break encapsulation. Thus having an interface with a field would effectively be coding to an implementation instead of an interface, which is a curious paradox for an interface to have!
接口不包含字段,因为字段代表数据表示的特定实现,暴露它们会破坏封装。因此,拥有一个带有字段的接口将有效地编码到一个实现而不是一个接口,这对于接口来说是一个奇怪的悖论!
For instance, part of your Year
specification might require that it be invalid for ICar
implementers to allow assignment to a Year
which is later than the current year + 1 or before 1900. There's no way to say that if you had exposed Year
fields -- far better to use properties instead to do the work here.
例如,您的Year
规范的一部分可能要求ICar
实现者允许分配给Year
比当前年份 + 1 或 1900 年之前的a是无效的。 没有办法说如果您公开了Year
字段 - 使用更好属性而不是在这里完成工作。
回答by John Feminella
For this you can have a Car base class that implement the year field, and all other implementations can inheritance from it.
为此,您可以拥有一个实现年份字段的 Car 基类,并且所有其他实现都可以从中继承。
回答by LBushkin
Interfaces in C# are intended to define the contract that a class will adhere to - not a particular implementation.
C# 中的接口旨在定义类将遵守的契约 - 而不是特定的实现。
In that spirit, C# interfaces do allowproperties to be defined - which the caller must supply an implementation for:
本着这种精神,C# 接口确实允许定义属性 - 调用者必须为其提供实现:
interface ICar
{
int Year { get; set; }
}
Implementing classes can use auto-properties to simplify implementation, if there's no special logic associated with the property:
如果没有与属性关联的特殊逻辑,实现类可以使用自动属性来简化实现:
class Automobile : ICar
{
public int Year { get; set; } // automatically implemented
}
回答by Adam Robinson
The short answer is yes, every implementing type will have to create its own backing variable. This is because an interface is analogous to a contract. All it can do is specify particular publicly accessible pieces of code that an implementing type must make available; it cannot contain any code itself.
简短的回答是肯定的,每个实现类型都必须创建自己的支持变量。这是因为接口类似于合同。它所能做的就是指定实现类型必须提供的特定可公开访问的代码段;它本身不能包含任何代码。
Consider this scenario using what you suggest:
使用您的建议考虑这种情况:
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 { }
We have a couple of problems here:
我们在这里有几个问题:
- Because all members of an interface are--by definition--public, our backing variable is now exposed to anyone using the interface
- Which
myBackingVariable
willMyClass
use?
- 因为接口的所有成员——根据定义——都是公共的,我们的后备变量现在向使用该接口的任何人公开
- 哪个
myBackingVariable
会MyClass
用?
The most common approach taken is to declare the interface and a barebones abstract class that implements it. This allows you the flexibility of either inheriting from the abstract class and getting the implementation for free, or explicitly implementing the interface and being allowed to inherit from another class. It works something like this:
最常用的方法是声明接口和实现它的准系统抽象类。这允许您灵活地从抽象类继承并免费获得实现,或者显式实现接口并允许从另一个类继承。它的工作原理是这样的:
public interface IMyInterface
{
int MyProperty { get; set; }
}
public abstract class MyInterfaceBase : IMyInterface
{
int myProperty;
public int MyProperty
{
get { return myProperty; }
set { myProperty = value; }
}
}
回答by Amit
Interfaces do not contain any implementation.
接口不包含任何实现。
- Define an interface with a property.
- Further you can implement that interface in any class and use this class going forward.
- If required you can have this property defined as virtual in the class so that you can modify its behaviour.
- 定义具有属性的接口。
- 此外,您可以在任何类中实现该接口并继续使用该类。
- 如果需要,您可以在类中将此属性定义为虚拟,以便您可以修改其行为。
回答by Frode N. Rosand
An interface defines publicinstance properties and methods. Fields are typically private, or at the most protected, internal or protected internal (the term "field" is typically not used for anything public).
接口定义公共实例属性和方法。字段通常是私有的,或者最多是受保护的内部或受保护的内部(术语“字段”通常不用于任何公共的东西)。
As stated by other replies you can define a base class and define a protected property which will be accessible by all inheritors.
正如其他回复所述,您可以定义一个基类并定义一个所有继承者都可以访问的受保护属性。
One oddity is that an interface can in fact be defined as internalbut it limits the usefulness of the interface, and it is typically used to define internal functionality that is not used by other external code.
一个奇怪的是,接口实际上可以定义为内部接口,但它限制了接口的有用性,它通常用于定义其他外部代码不使用的内部功能。
回答by Hans Passant
Eric Lippert nailed it, I'll use a different way to say what he said. All of the members of an interface are virtual and they all need to be overridden by a class that inherits the interface. You don't explicitly write the virtual keyword in the interface declaration, nor use the override keyword in the class, they are implied.
埃里克·利珀特 (Eric Lippert) 说得对,我会用不同的方式来表达他所说的话。接口的所有成员都是虚拟的,它们都需要被继承接口的类覆盖。您没有在接口声明中显式地编写 virtual 关键字,也没有在类中使用 override 关键字,它们是隐含的。
The virtual keyword is implemented in .NET with methods and a so-called v-table, an array of method pointers. The override keyword fills the v-table slot with a different method pointer, overwriting the one produced by the base class. Properties, events and indexers are implemented as methods under the hood. But fields are not. Interfaces can therefore not contain fields.
virtual 关键字在 .NET 中通过方法和所谓的 v-table(方法指针数组)实现。override 关键字用不同的方法指针填充 v-table 槽,覆盖基类生成的方法指针。属性、事件和索引器在幕后实现为方法。但字段不是。因此接口不能包含字段。
回答by zomf
Others have given the 'Why', so I'll just add that your interface can define a Control; if you wrap it in a property:
其他人给出了“为什么”,所以我将补充一点,您的界面可以定义一个控件;如果你把它包装在一个属性中:
public interface IView {
Control Year { get; }
}
public Form : IView {
public Control Year { get { return uxYear; } } //numeric text box or whatever
}