当前位置: 首页 > 编程语言 > C#

C#为什么要覆盖静态方法分享

时间:2023-04-10 20:56:29 C#

C#为什么要覆盖静态方法protectedstaticnewvoidWhyIsThisValidCode(){}为什么允许覆盖静态方法?除了bug,它根本无法按照您认为的方式工作。参加以下课程。类BaseLogger{protectedstaticstringLogName{get{returnnull;}}publicstaticvoidLog(stringmessage){记录器。日志(消息,日志名称);}}类SpecificLogger:BaseLogger{protectedstaticstringLogName{get{return"Specific";}}}这是代码SpecificLogger.Log("test");是的,但它不会按照你看代码的想法去做。它使用LogName=null调用Logger.Log。那么为什么允许这样做呢?new关键字不会覆盖方法。相反,它创建了一个与原始方法无关的同名新方法。无法覆盖静态方法,因为它们不是虚拟的您没有覆盖它,而是隐藏了它。普通方法会表现出完全相同的行为,因此这里没有特定于静态方法的内容。隐藏仅在少数情况下有用。我经常遇到的方法是在派生类中使返回类型更具体。但我从不使用静态方法。具有特定名称的静态函数可能有用的一个领域是,如果您使用反射并希望通过从方法返回来获取每个类的信息。但当然,在大多数情况下,属性更适合。而且它不可能产生错误,因为您的代码会产生编译器警告:警告“StaticHiding.SpecificLogger.LogName”隐藏了继承的成员“StaticHiding.BaseLogger.LogName”。如果要隐藏,请使用new关键字。如果你使用new你应该知道你在做什么。其他人指出它不是压倒一切的,但这仍然留下您最初的问题:为什么您可以这样做?(但问题是“为什么可以隐藏静态方法”。)支持包含基类的组件和使用这些基类的组件的独立版本控制是一个不可避免的特性。例如,假设一个组件CompB包含一个基类,另一个组件CompD包含一个派生类。在CompB的版本1中,可能没有任何名为LogName的属性。CompD的作者决定添加一个名为LogName的静态属性。此时要理解的关键是,CompDv1的作者并不打算替换或隐藏基类的任何功能——在编写代码时,基类中没有名为LogName的成员。现在假设发布了新版本的CompB库。在这个新版本中,作者添加了一个LogName属性。CompD应该怎么办?选项似乎是:CompD不再有效,因为它引入的LogName与添加到CompB的LogName冲突以某种方式使CompD的LogName替换基本的CompBLogName。(对于静态变量它是如何工作的并不是很清楚。你可以想象它对于非静态变量。)将两个LogName成员视为具有相同名称的完全不同的成员。(实际上,它们不是-它们称为BaseLogger.LogName和SpecificLogger.LogName。但由于在C#中我们并不总是需要对成员名称进行类限定,因此看起来它们具有相同的名称。).NET选择做3.(它用静态和非静态来做。如果你想要行为2-替换-用非静态,那么基础必须是虚拟的,并且派生类必须将方法标记为重写以明确它们是故意的覆盖基类方法中的方法。C#永远不会用派生类的方法替换基类的方法,除非派生类明确声明这是他们想要的。)这可能是安全的,因为这两个成员是不相关的-基本LogName在引入派生的地方甚至不存在。这比简单的hack更可取,因为基类的最新版本引入了一个新成员。如果没有此功能,新版本的.NETFramework将无法在不破坏更改的情况下向现有基类添加新成员。你说这种行为不是你所期望的。实际上,这正是我正在寻找的,也是您在实践中可能想要的。BaseLogger不知道SpecificLogger引入了自己的LogName属性。(没有机制,因为你不能重写静态方法。)当SpecificLogger的作者写下LogName属性时,请记住他们正在编写没有LogName的BaseLoggerv1,所以他们不打算用它替换基本方法。由于两个类都不想替换,显然替换是错误的。唯一一次遇到这种情况是因为这两个类位于不同的组件中。(很明显,当它们在同一个组件中时,您可以设计一个场景,但是为什么要这样做?如果您拥有两段代码并将它们发布在一个组件中,那么这样做会很疯狂。)所以BaseLogger应该有自己的LogName属性,这正是发生的事情。你可能写过:SpecificLogger.Log("test");但是C#编译器发现SpecificLogger没有提供Log方法,所以它把它变成:BaseLogger.Log("test");因为那是定义Log方法的地方。因此,每当您在派生类中定义一个不尝试覆盖现有方法的方法时,C#编译器都会在元数据中指出这一点。(方法元数据中有一个“newslot”设置,该方法是全新的,与基类中的任何内容都无关。)但是,如果您要重新编译CompD,这会给您带来问题。假设由于一些完全不相关的代码,你有一些错误报告,你需要发布一个新版本的CompD。您可以针对新版本的CompB对其进行编译。如果您正在编写的代码不被允许,那么您实际上不能——旧代码已被编译为可以工作,但您将无法编译该代码的新版本,这有点疯狂。所以,为了支持这个(坦率地说有点模糊)场景,他们允许你这样做。他们会警告你这里有命名冲突。你需要提供新的关键字来摆脱它。这是一个晦涩的场景,但如果你想支持跨组件边界的继承,就需要这样做,否则在基类上添加一个新的public或protected成员将不可避免地是一个破坏性的变化。这就是它在这里的原因。但是依赖它是不好的做法,因此您会收到编译器警告。使用new关键字来抑制警告应该只是权宜之计。底线是:这个特性的存在只有一个原因,那就是在某些基类的新版本添加了一个以前不存在的成员并与现在的成员发生冲突的情况下让你摆脱困境已经在你的派生类上了。如果不是您的情况,请不要使用此功能。(我认为他们实际上应该发出错误而不是警告,以便在您遗漏新错误时更加清楚。)静态方法和字段不属于类实例,它们属于类定义。静态方法不参与虚调度机制,也不属于虚方法表。它们只是该特定类的方法和字段。看起来方法和字段可能是“继承的”,因为您可以执行SpecificLogger.Log(),但这只是让您不必一直引用基类的事情。静态方法和字段实际上只是全局方法和字段,只是OO类型。您没有覆盖基类中的属性,而是隐藏了它。运行时实际使用的属性取决于您使用的接口。以下示例说明:SpecificLoggera=newSpecificLogger();BaseLoggerb=newSpecificLogger();控制台.Write(a.Log);//具体Console.Write(b.Log);//null在您的代码中,Log方法实际上以BaseLogger接口为目标-因为Log方法是BaseLogger类的一部分。静态方法和属性不能被覆盖,当你想隐藏一个属性时,你应该使用new关键字来表明你正在隐藏一些东西。令我惊讶的是,在.netFramework4.5.1上,VS2013允许并编译没有任何错误。以上就是C#学习教程:为什么涵盖了C#中静态方法分享的所有内容,如果对大家有用,还需要详细了解C#学习教程,希望大家多多关注—classA{publicstaticvoidFoo(){}}classB:A{}classProgram{staticvoidmain(string[]args){B.Foo();}}本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处: