C# 是否存在Java的@Override的C等价物?

C# 是否存在Java的@Override的C等价物?,c#,interface,overriding,abstract,C#,Interface,Overriding,Abstract,以前有人问过这个问题,但我无法从这个答案中得到清晰的答案,这就是为什么我再次问 让我们举两个例子: 类实现接口 类扩展了一个抽象类 我的感觉是,关于override关键字,两个示例的行为必须相同。覆盖的预期目标是什么?防止方法在超类或接口中被丢弃,而不会在所有子类或实现类中被更改。因此,需要进行编译时代码一致性检查 在此C代码中,编译会导致错误:“……RepositoryContext.getXmlDoc”:找不到可重写的合适方法: interface IRepositoryContext {

以前有人问过这个问题,但我无法从这个答案中得到清晰的答案,这就是为什么我再次问

让我们举两个例子:

类实现接口 类扩展了一个抽象类 我的感觉是,关于override关键字,两个示例的行为必须相同。覆盖的预期目标是什么?防止方法在超类或接口中被丢弃,而不会在所有子类或实现类中被更改。因此,需要进行编译时代码一致性检查

在此C代码中,编译会导致错误:“……RepositoryContext.getXmlDoc”:找不到可重写的合适方法:

interface IRepositoryContext
{
    XmlDocument getXmlDoc();
}

class RepositoryContext : IRepositoryContext
{
     private readonly XmlDocument gXmlDoc = new XmlDocument();

     public override XmlDocument getXmlDoc() // does not compile
     {    
        return gXmlDoc;
     }
}
而在此C代码中,编译工作没有任何错误或警告:

abstract class RepositoryContextBase
{

     public abstract XmlDocument getXmlDoc();
}

class RepositoryContext : RepositoryContextBase
{
     private readonly XmlDocument gXmlDoc = new XmlDocument();

     public override XmlDocument getXmlDoc()
     {    
        return gXmlDoc;
     }
}

这是否是一个有效的假设,即它不应该以相同的方式工作,或者是否有一种解决方法,或者……?

在您的第一个示例中,您正在实现一个接口。在这种情况下,您不必指定override关键字,只需将其删除。

在第一个示例中,您正在实现一个接口。在这种情况下,您不必指定override关键字,只需将其删除。

对于类中的虚拟或抽象方法,您需要插入override关键字,否则它将无法工作

对于接口,没有等价物

然而,接口实现必须实现它们的所有基本方法,因此忘记一个方法通常会导致编译器错误。
这就不那么重要了。

对于类中的虚方法或抽象方法,需要插入override关键字,否则它根本不起作用

对于接口,没有等价物

然而,接口实现必须实现它们的所有基本方法,因此忘记一个方法通常会导致编译器错误。 这样就不那么重要了。

覆盖修饰符的定义如下:

重写修饰符是扩展或修改继承的方法、属性、索引器或事件的抽象或虚拟实现所必需的

override关键字指定该方法覆盖现有的方法实现,这就是为什么在直接实现接口时不需要指定它的原因-没有这样的方法可以覆盖;你是第一个实现它的人

当您使用override关键字时,本质上是说对于这个类,调用这个方法而不是基方法。这显然不适用于没有此类基本方法的情况,例如直接实现接口时。

覆盖修饰符的定义如下:

重写修饰符是扩展或修改继承的方法、属性、索引器或事件的抽象或虚拟实现所必需的

override关键字指定该方法覆盖现有的方法实现,这就是为什么在直接实现接口时不需要指定它的原因-没有这样的方法可以覆盖;你是第一个实现它的人


当您使用override关键字时,本质上是说对于这个类,调用这个方法而不是基方法。这显然不适用于没有这种基本方法的情况,例如,当您直接实现一个接口时。

在第一个示例中,它是您正在实现的一个接口。当您是继承链中唯一的实现者时,您不能重写某些内容

在第二个示例中,您继承了一个具体的实现,并声明您希望实现抽象成员及其语法,尽管从字面上讲,它不是一个覆盖,而实现是override关键字。然而,实际上,您正在覆盖您所属的链,因为您正在实现它

因此,请考虑override关键字,这与您确保在对继承器实例调用实现时调用实现而不是基类有关

这也解释了为什么您必须在覆盖内显式调用base.Member,因为您已经覆盖了链

另一个需要记住的OO概念是,在非抽象或虚拟的方法上也可以实现同样的效果。事实上,成员可以隐藏,您不必使用new关键字指定它们


话虽如此,这将有助于您抽象出这样一个概念,即这些只是语言功能,或者更好地说,它只是语法。

在第一个示例中,它是您正在实现的一个接口。当您是继承链中唯一的实现者时,您不能重写某些内容

在第二个示例中,您继承了一个具体的实现,并声明您希望实现抽象成员及其语法,尽管从字面上讲,它不是一个覆盖,而实现是override关键字。然而,实际上,您正在覆盖您所属的链,因为您正在实现它

因此,考虑override关键字更多地与以下事实相关: 确保在对继承器的实例调用实现时,调用实现而不是基类

这也解释了为什么您必须在覆盖内显式调用base.Member,因为您已经覆盖了链

另一个需要记住的OO概念是,在非抽象或虚拟的方法上也可以实现同样的效果。事实上,成员可以隐藏,您不必使用new关键字指定它们


话虽如此,这将有助于你抽象出这样一个概念,即这些仅仅是语言特性,或者更好地说是语法。

似乎你对接口实现和继承有一个误解

接口实现与继承完全不同。对于接口,您可以静态地(即在编译时)强制存在某些方法签名。因此,在这样的上下文中,像override之类的任何关键字都是完全错误的。 相反,继承通过虚拟方法表(基本上是方法地址列表)导致运行时多态性。
您也可以从以下事实中看到这一点:在C中,您可以实现任意多个接口,而多重继承是被禁止的。

似乎您对接口实现与继承有一个错误的概念

接口实现与继承完全不同。对于接口,您可以静态地(即在编译时)强制存在某些方法签名。因此,在这样的上下文中,像override之类的任何关键字都是完全错误的。 相反,继承通过虚拟方法表(基本上是方法地址列表)导致运行时多态性。
您也可以从以下事实中看到这一点:在C中,您可以实现任意多个接口,而多重继承是被禁止的。

原因是实现接口和重写方法之间存在根本区别

为了完全实现一个接口,您必须为所有方法和/或属性提供实现,但这些实现不一定反过来是可重写的。编译器希望您在创建方法时能够非常明确地表达自己的意图,因为您可能会想到一系列行为中的一种,并且它希望确定您指的是哪一种

关键字表示我正在用这个重写基类的实现。如果在实现接口时没有基本实现,那么它就不适用。您可以使用virtual来指示没有基本实现的可重写方法,否则将忽略override和virtual

因此,考虑到这个接口:

interface IFoo
{
    void Bar();
}
该类实现该接口,并允许类从该接口继承并重写该实现,因为与Java不同,C中的方法在默认情况下不是虚拟的:

class Foo : IFoo
{
    public virtual void Bar() { ... } // compiles
}

class DerivedFoo : Foo
{
    public override void Bar() { ... } // compiles, and may choose to call base.Bar()
}
鉴于此类实现了该接口,并且不允许重写:

class Foo : IFoo
{
    public void Bar(); // compiles
}

class DerivedFoo : Foo
{
    public override void Bar() { ... } // does NOT compile; Foo.Bar() is not virtual (overrideable)
}
事实上,还有更多的可能性,包括:

您可以创建实现接口的基类,但只能为某些/所有方法提供抽象实现。 您可以实现一个接口方法 可以使用重写方法来防止进一步的重写 可以使用与该名称的基类方法无关的相同名称创建 有

如果您对编译器不够具体,它将警告您或抛出错误

更新

编译器在上面的第二个示例中抱怨的原因是,您不会得到多态行为。也就是说,如果有人引用Foo并调用Bar,他们将得到Foo的实现,而不是DerivedFoo的。这是因为Bar.Foo不在虚拟方法表中。换句话说,在C语言中,与Java相比,默认情况是所有方法都是最终的,除非您另有说明

从您的评论来看,在我上面的第一个示例中,您试图获得警告或错误,然后通过完全删除Bar方法来更改IFoo。显然,如果您只是更改方法签名,您将得到一个您希望的合适的编译错误

您可以通过实现以下方法来实现这一点:

class Foo : IFoo
{
    void IFoo.Bar() { ... }
}
然后,如果接口发生更改,您将得到一个编译错误。然而,这意味着派生类不能再重写Foo的实现;如果你也想要这种行为,你需要:

class Foo : IFoo
{
    void IFoo.Bar() { ... }

    protected /* or public */ virtual void Bar()
    {
        IFoo foo = this; // declare rather than cast, to get compile error not runtime exception
        foo.Bar();
    }
}
如果从显式实现和其他实现中删除该方法,仍然会出现编译错误

请记住,显式实现仅对引用IFoo而不是Foo的调用方可用。但是,如果在上面的代码中添加了一个公共方法,例如,委托给显式IFoo实现,那么这不会是一个问题,并且它不必是虚拟的,除非您希望它可以重写

这是一种行之有效的方法;这是否过火是一个品味的问题,但我可以看出
e在重构过程中删除冗余代码的优点,前提是类不是公共的和/或不在程序集外部使用。但是,我建议不要以这种方式分解代码,而是使用这样的工具,它会警告您未使用的方法。

原因是实现接口和重写方法之间存在根本区别

为了完全实现一个接口,您必须为所有方法和/或属性提供实现,但这些实现不一定反过来是可重写的。编译器希望您在创建方法时能够非常明确地表达自己的意图,因为您可能会想到一系列行为中的一种,并且它希望确定您指的是哪一种

关键字表示我正在用这个重写基类的实现。如果在实现接口时没有基本实现,那么它就不适用。您可以使用virtual来指示没有基本实现的可重写方法,否则将忽略override和virtual

因此,考虑到这个接口:

interface IFoo
{
    void Bar();
}
该类实现该接口,并允许类从该接口继承并重写该实现,因为与Java不同,C中的方法在默认情况下不是虚拟的:

class Foo : IFoo
{
    public virtual void Bar() { ... } // compiles
}

class DerivedFoo : Foo
{
    public override void Bar() { ... } // compiles, and may choose to call base.Bar()
}
鉴于此类实现了该接口,并且不允许重写:

class Foo : IFoo
{
    public void Bar(); // compiles
}

class DerivedFoo : Foo
{
    public override void Bar() { ... } // does NOT compile; Foo.Bar() is not virtual (overrideable)
}
事实上,还有更多的可能性,包括:

您可以创建实现接口的基类,但只能为某些/所有方法提供抽象实现。 您可以实现一个接口方法 可以使用重写方法来防止进一步的重写 可以使用与该名称的基类方法无关的相同名称创建 有

如果您对编译器不够具体,它将警告您或抛出错误

更新

编译器在上面的第二个示例中抱怨的原因是,您不会得到多态行为。也就是说,如果有人引用Foo并调用Bar,他们将得到Foo的实现,而不是DerivedFoo的。这是因为Bar.Foo不在虚拟方法表中。换句话说,在C语言中,与Java相比,默认情况是所有方法都是最终的,除非您另有说明

从您的评论来看,在我上面的第一个示例中,您试图获得警告或错误,然后通过完全删除Bar方法来更改IFoo。显然,如果您只是更改方法签名,您将得到一个您希望的合适的编译错误

您可以通过实现以下方法来实现这一点:

class Foo : IFoo
{
    void IFoo.Bar() { ... }
}
然后,如果接口发生更改,您将得到一个编译错误。然而,这意味着派生类不能再重写Foo的实现;如果你也想要这种行为,你需要:

class Foo : IFoo
{
    void IFoo.Bar() { ... }

    protected /* or public */ virtual void Bar()
    {
        IFoo foo = this; // declare rather than cast, to get compile error not runtime exception
        foo.Bar();
    }
}
如果从显式实现和其他实现中删除该方法,仍然会出现编译错误

请记住,显式实现仅对引用IFoo而不是Foo的调用方可用。但是,如果在上面的代码中添加了一个公共方法,例如,委托给显式IFoo实现,那么这不会是一个问题,并且它不必是虚拟的,除非您希望它可以重写


这是一种行之有效的方法;这是否过份是一个品味问题,但我可以看到在重构过程中删除冗余代码的好处,只要这些类不是公共的和/或不在程序集外部使用。但是,我建议不要以这种方式分解代码,而是使用一种工具,例如,它会警告您有关未使用的方法。

所有人:我有一个答案:它不存在,所有认为它不相关的人也不存在。这是相关的。我不想对此一无所知,但如果您正在构建一个新的应用程序,很容易说:通过代码片段或其他方式从这个接口实现所有方法。但是,如果您有一个巨大的应用程序,其中您更改了一个接口方法签名,并且有许多类实现了多个接口,那么对于我来说,这是用java实现的,以确保在更改接口时不会忘记更改/删除一个方法。但我否认从概念上说这是因为编译器想要检查方法签名。。。不,区别在于概念上接口只定义责任,而抽象类可以定义如何实现责任。在我看来,这就是概念上的差异。从这个角度来看,对我来说,覆盖的含义没有什么不同。不过,非常感谢上面的评论。我将通过他们,探索隐藏等。谢谢@我很高兴看到你有一种感觉。但请注意,它可能与事实大不相同……对所有人来说:我有一个答案:它不在那里,对所有认为它不相关的人来说。这是相关的。我不想无知,但如果
您正在构建一个新的应用程序,很容易说:通过代码片段或其他方式从这个接口实现所有方法。但是,如果您有一个巨大的应用程序,其中您更改了一个接口方法签名,并且有许多类实现了多个接口,那么对于我来说,这是用java实现的,以确保在更改接口时不会忘记更改/删除一个方法。但我否认从概念上说这是因为编译器想要检查方法签名。。。不,区别在于概念上接口只定义责任,而抽象类可以定义如何实现责任。在我看来,这就是概念上的差异。从这个角度来看,对我来说,覆盖的含义没有什么不同。不过,非常感谢上面的评论。我将通过他们,探索隐藏等。谢谢@我很高兴看到你有一种感觉。但请注意,这可能与事实有很大不同……谢谢El Zorko。。你让我思考!,谢谢你。。。我会玩它,然后回到这个。。。我希望明天/后天。我能问一下你有什么不相信的吗?我的解释我能理解,或者为什么C在这些情况下需要这些关键字我也能理解,因为这只是语言设计的选择?oef,我在word中写了一些东西,但是只能是600个字符:第1部分:尝试阅读您所说的内容:引用:C语言的设计目的是使基类和派生类之间的版本控制能够不断发展并保持向后兼容性。C完全支持在基类中引入与派生类中的成员同名的新成员,并且不会导致意外行为。它还意味着类必须显式声明是否:1。方法旨在重写继承的=>使用“重写”。2.方法是一个隐藏super.sameMethod=>使用“new”的新方法。--第2部分:@您的备注1:重写方法实现接口:确保这是100%正确的。从概念上讲,对我来说:实现一个接口意味着:能够像。。。多态性。一个类,例如HumanBeing,可以实现多个接口:IEmployee、IHusband、IChoirMember。。。它是同一个实现类,可能是理论上的,但可以实现。而override方法通常在重写实现时执行。Bcz如果签名不能更改,在子接口中重写接口方法有什么用---谢谢El Zorko。。你让我思考!,谢谢你。。。我会玩它,然后回到这个。。。我希望明天/后天。我能问一下你有什么不相信的吗?我的解释我能理解,或者为什么C在这些情况下需要这些关键字我也能理解,因为这只是语言设计的选择?oef,我在word中写了一些东西,但是只能是600个字符:第1部分:尝试阅读您所说的内容:引用:C语言的设计目的是使基类和派生类之间的版本控制能够不断发展并保持向后兼容性。C完全支持在基类中引入与派生类中的成员同名的新成员,并且不会导致意外行为。它还意味着类必须显式声明是否:1。方法旨在重写继承的=>使用“重写”。2.方法是一个隐藏super.sameMethod=>使用“new”的新方法。--第2部分:@您的备注1:重写方法实现接口:确保这是100%正确的。从概念上讲,对我来说:实现一个接口意味着:能够像。。。多态性。一个类,例如HumanBeing,可以实现多个接口:IEmployee、IHusband、IChoirMember。。。它是同一个实现类,可能是理论上的,但可以实现。而override方法通常在重写实现时执行。Bcz如果签名不能更改,在子接口中重写接口方法有什么用---