接口中的属性

本文关键字:属性 接口 | 更新日期: 2023-09-27 17:55:43

在我的

界面中,我已经声明了一个带有setter和getter的属性。

public interface ITestInterface
{
   string AProperty { get; set; }
}

当我编写继承该接口的类时,为什么我需要再次定义这两个属性?

public sealed class MyClass: ITestInterface
{
   public string AProperty { get; set; }
}

接口中的属性

因为您不是从接口继承的,所以您正在实现该接口。(尽管它们共享相同的语法:

public class MyClass : IMyInterface { ... } //interface implementing
public class MyClass : MyBaseClass { ... } //inheriting from a class
假设你继承了一个糖果盒(

不是从你的祖先那里继承的,以编程方式),它(不完全)就像你把糖果盒放在另一个盒子里一样,现在外盒子(派生类,继承的)是从糖果盒继承的,并拥有糖果盒拥有的所有东西,但如果你想自己实现(制作)一个糖果盒,你必须构建一个盒子并在里面放一些糖果。这是接口的工作方式。

您的接口定义仅告知存在具有 getter 和 setter 的属性,而不是它是如何实现的。可以使用自动实现的属性,但不是必需的。

在接口之后,这将是一个有效的实现:

public sealed class MyClass: ITestInterface
{
    public string APROPERTY
    {
        get { return someField + " hello"; }
        set { someOtherField = value; }
    }
}

在接口定义中,string AProperty { get; set; }是属性的声明,而在类中,它表示属性是自动实现的。

简答

因为接口只包含类的定义,并且不能包含任何成员函数的实际实现。这是设计使然。

长答案

首先,您必须意识到属性基本上是使用一些简化的语法获取和设置成员函数。因此,这里的问题是:为什么接口定义不能包含成员函数的实现?

好吧,在某些语言中(最值得注意的是:C++),您可以。

如果你有一个继承链,这基本上是通过查找表来解决的。假设你有成员函数 1,那么在继承链中的所有类中,都有一个表,其中包含指向函数 1 的指针。调用成员函数后,调用基本上会从属于对象类型的表中获取第一个条目,然后调用该条目。这个东西被称为vtable(有关更多详细信息,请参阅此处)。

现在,在C++中,VTables 对开发人员非常透明:每个类基本上都有一个 vtable,并且没有真正的"接口"这样的东西。这也意味着所有类都可以具有实现和成员,例如字段。如果你有一个只有纯虚拟成员的类(例如没有实现的函数),你就C++等价于"接口"。

在软件工程中,这些类通常被称为"接口"类,因为它们只包含正在发生的事情的定义,而不是实际的实现。接口有一个很好的属性,即它们描述功能而不实际涉及细节,从而可以在代码中放置"边界"。这有很多用例,包括 (RPC) 通信、许多设计模式等。

在C++中,一个类可以在有和没有实现的情况下从多个类(多重继承)派生。此外,由于接口实际上更像是 C# 中的"抽象"类,而不是"接口",这意味着您还可以在其中添加功能。因此,前面描述的 vtable 包含指向所有基类中的函数的指针。

当您开始向接口类添加功能时,此问题就开始了。首先,假设你有这样的东西(我将在C#中执行此操作):

interface A { Foo(); } // basically an interface.
interface B : A { Foo(); } // another interface
class B : A { void Foo() {...} } // implementation of Foo, inherits A
class D : B,C { } // inherits both B, C (and A via both B and C).

我们在这里感兴趣的是,如果您在课堂上D中称Foo会发生什么。为此,我们必须为类 D 构造一个 vtable 。基本上这个 vtable 看起来像这样:

Foo() -> C::Foo()

这意味着如果你构造一个 D 的对象,并调用 Foo ,你最终将在类型 C 中调用 Foo 的实现:

var tmp = new D();
tmp.Foo(); // calls C::Foo()

当我们把B的定义改成这样的时候,就变得更加困难了:

class B : A { void Foo() {...} } // changed into an implementation

同样,我们尝试为类 D 构建 vtable,最终遇到了一个问题:

Foo() -> C::Foo() or B::Foo()???

我们在这里面临的问题是:在调用该成员时,我们将使用什么Foo实现?另外,我们要调用什么构造函数?那么破坏令呢?在C++中,有一些解决方法称为虚拟继承

在设计 .NET 和 C# 语言时,他们考虑了过去多重继承的经验以及虚拟继承的含义,并认为这不仅是一件难以实现的事情,而且对开发人员来说充其量也非常混乱。如您所见,当您只添加接口时,这些问题并不存在。

因此,这就是为什么您的界面中不能有属性(或方法)的原因。

我认为这里的问题是,相同的语法对接口和类有两种不同的含义。 AProperty { get; set; }接口中是仅声明,在类中它是自动实现的接口。

因此,该术语取决于上下文。

public interface ITestInterface
{
    string AProperty { get; set; }
}

声明属性,但无法实现它。

public sealed class MyClass: ITestInterface
{
    public string AProperty { get; set; }
}
实现

接口,其中属性自动实现(仅适用于类)。

接口包含属性签名,而不是实际定义。您实际上是在请求任何实现 ITestInterface 的类来实现 get 和 set for AProperty。有关更多详细信息,请参阅此和此内容。

正如其他人所说,接口只是您的方法和属性签名的容器。它需要实现,但此实现签名将与接口中使用的签名完美匹配。此外,它还保证所有这些成员都可以在类实例中访问,因为它们是默认的公共属性,并且没有实现程序将根本不编译。

假设你有接口:

public interface ITestInterface
{
    string AProperty { get; }
}

和实现它的类:

class MyClass : ITestInterface
{
   public string AProperty { get { if (DateTime.Today.Day > 7) return "First week of month has past"; return "First week of month is on"; } }
}
无法使用

自动实现的属性,也无法在此类中添加 setter,因为接口属性缺少 set 访问器,并且自动实现的属性要求接口包含自动实现的属性签名 ({ get; set;})。所以在你的例子中,接口只是声明属性,仅此而已。

只要知道类

继承了什么接口,你就知道那里有什么成员,如果你只想使用(或允许用户使用)其中一些方法(虽然不允许更改任何内容),你总是可以将你的类实例向上转换为这些接口类型之一,并将其作为参数传递。

我认为这里的混淆来自于这样一个事实,即自动属性(只是get和/或set声明)在接口和实现中看起来相同。接口只是类必须提供什么的声明(协定),以便被视为接口的实现者。如果您考虑接口中的方法声明与其在类中的实现,则更清楚。

接口 = 要求;类 = 如何满足这些要求

public interface ITestInterface
{
   string GetAProperty();
}
public class MyClass : ITestInterface
{
    public string GetAProperty()
    {
        // Do work...
        return "Value";
    }
}