C# 覆盖扩展方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/474074/
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
Overriding Extension Methods
提问by JoshRivers
I've been thinking about using extension methods as a replacement for an abstract base class. The extension methods can provide default functionality, and can be 'overridden' by putting a method of the same signature in a derived class.
我一直在考虑使用扩展方法来替代抽象基类。扩展方法可以提供默认功能,并且可以通过将具有相同签名的方法放在派生类中来“覆盖”。
Any reason I shouldn't do this?
有什么理由我不应该这样做?
Also, if I have two extension methods with the same signature, which one is used? Is there a way of establishing priority?
另外,如果我有两个具有相同签名的扩展方法,使用哪一个?有没有办法确定优先级?
采纳答案by Jacob Adams
I agree with Michael. Base classes should contain all base functionality Extension methods should, obviously, extend the base functionality. In dynamic languages like Ruby it is often typical to use extension methods to provide addition functionality instead of using subclasses. Basically, extension methods are there to replacing using subclassses, not to replace using base classes.
我同意迈克尔的看法。基类应该包含所有基本功能扩展方法显然应该扩展基本功能。在像 Ruby 这样的动态语言中,通常使用扩展方法来提供附加功能而不是使用子类。基本上,扩展方法是使用子类替换,而不是使用基类替换。
The only exception to this that I've seen is if you have multiple type that have different class hierachies (like winform controls), you can make a subclass of each that all implement and interface and then extend that interface, thereby giving "base" functionality to a group of different controls, without extending everything like Control or Object.
我见过的唯一例外是,如果您有多个具有不同类层次结构的类型(如 winform 控件),您可以创建每个都实现和接口的子类,然后扩展该接口,从而提供“基础”一组不同控件的功能,而无需扩展控件或对象等所有内容。
Edit: answering your second question
编辑:回答你的第二个问题
I think the compiler will catch this for you.
我认为编译器会为你捕捉到这一点。
回答by Michael Meadows
In general, you shouldn't provide "base" functionality through extension methods. They should only be used to "extend" class functionality. If you have access to the base class code, and the functionality you're trying to implement is logically part of the inheritance heirarchy, then you should put it in the abstract class.
通常,您不应通过扩展方法提供“基本”功能。它们应该只用于“扩展”类功能。如果您可以访问基类代码,并且您尝试实现的功能在逻辑上是继承层次结构的一部分,那么您应该将它放在抽象类中。
My point is, just because you candoesn't mean you should. It's often best just to stick with good old fashioned OOP and use the newer language features when plain old OO programming falls short of providing you a reasonable solution.
我的观点是,仅仅因为你可以并不意味着你应该。当普通的老式 OO 编程无法为您提供合理的解决方案时,通常最好坚持使用老式的 OOP 并使用较新的语言功能。
回答by Greg D
They're semantically different operations. For example, polymorphism may not work the way it would with an abstract base class.
它们在语义上是不同的操作。例如,多态性可能不会像抽象基类那样工作。
As a general rule, use any language tool for what it's designed for. Extension methods aren't a replacement for inheritance, they're a technique to extend the functionality of a class using (typically) it's already-visible interface.
作为一般规则,请根据其设计目的使用任何语言工具。扩展方法不是继承的替代品,它们是一种使用(通常)已经可见的接口来扩展类功能的技术。
回答by yfeldblum
There's every reason you shouldn't do it. The first of which is, you can't guarantee how your extensions will be called:
你有充分的理由不这样做。第一个是,你不能保证你的扩展将如何被调用:
MyExtensions.AMethod(myObj)
or
或者
myObj.AMethod()
The second is merely syntactic sugar for the first.
第二个只是第一个的语法糖。
What you suggest goes against the spirit of the language feature. Extension methods are decidedly notobject-oriented. Yet you are trying to achieve an object-oriented technique. Don't use extension methods for that.
你的建议违背了语言功能的精神。扩展方法显然不是面向对象的。然而,您正在尝试实现面向对象的技术。不要为此使用扩展方法。
回答by Dave Ray
This is definitely a bad idea. Extension methods are bound statically which means that, unless your calling the override on an object whose compile-time type is the sub-type, you'll still continue to call the extension method. Bye-bye polymorphism. This pagehas a good discussion of the perils of extension methods.
这绝对是个坏主意。扩展方法是静态绑定的,这意味着,除非您在编译时类型为子类型的对象上调用覆盖,否则您仍将继续调用扩展方法。再见多态性。这个页面很好地讨论了扩展方法的危险。
回答by Oren Ben-Kiki
All answers here stated "you can't", which is true as far as it goes. Most added "and you shouldn't". I would like to make the case that you should be able to - small comfort as this may be.
这里的所有答案都表示“你不能”,就目前而言,这是正确的。大多数人补充说“你不应该”。我想说明你应该能够——尽管这可能是一种小小的安慰。
Take a painful real world example: if you are unfortunate enough to be using the new MVC framework, and your view code is using some HtmlHelper extension method all over the place, and you want to override its default behavior... what then?
举一个痛苦的现实世界的例子:如果你不幸使用了新的 MVC 框架,并且你的视图代码到处都在使用一些 HtmlHelper 扩展方法,并且你想覆盖它的默认行为......那怎么办?
You are SOL, that's what. Even if you did the "OOP thing" - derive from HtmlHelper, change your base view class to replace the 'Html' object instance with an instance of your DerivedHtmlHelper, and define an explicit 'Foo' method in it - even if you did all that, calling 'Html.Foo' will stillinvoke the original extension method and not method.
你是SOL,就是这样。即使你做了“OOP 事情”——从 HtmlHelper 派生,改变你的基本视图类,用你的 DerivedHtmlHelper 的实例替换“Html”对象实例,并在其中定义一个显式的“Foo”方法——即使你做了所有也就是说,调用 'Html.Foo'仍将调用原始扩展方法而不是方法。
This is surprising! After all, extension methods are only expected to be applied if the object does not already have a method! What's going on here?
这令人惊讶!毕竟,只有在对象还没有方法时才期望应用扩展方法!这里发生了什么?
Well, this because extension methods are a staticfeature. That is, when seeing 'Html.Foo', the compiler looks at the statictype of 'Html'. If it has a 'Foo' method, it is called as usual. Otherwise, if there is 'SomeClass' providing a 'Foo' extension method, it converts the expression to 'SomeClass.Foo(Html)'.
嗯,这是因为扩展方法是一个静态特性。也就是说,当看到'Html.Foo'时,编译器查看的是'Html'的静态类型。如果它有一个 'Foo' 方法,它会像往常一样被调用。否则,如果有 'SomeClass' 提供了一个 'Foo' 扩展方法,它会将表达式转换为 'SomeClass.Foo(Html)'。
What you would expect is that the compiler would consider the dynamictype of the object. That is, that the generated (pseudo-)code would read 'Html.HasMethod("Foo") ? Html.Foo() : SomeClass.Foo(Html)'.
您所期望的是编译器会考虑对象的动态类型。也就是说,生成的(伪)代码将读取 'Html.HasMethod("Foo") ?Html.Foo() : SomeClass.Foo(Html)'。
This of course would incur the cost of using reflection in each extension method call. So, you would expect that you could write something like 'static void Foo(virtualthis HtmlHelper html)' to explicitly request the compiler to insert the run-time check. Call this a "virtual extension method".
这当然会导致在每个扩展方法调用中使用反射的成本。因此,您可能希望您可以编写类似“static void Foo( virtualthis HtmlHelper html)”之类的内容来显式请求编译器插入运行时检查。称其为“虚拟扩展方法”。
However, in their limited budget and infinite wisdom, the C# language designers went with only the more efficient, more restricted alternative. Which leaves me still SOL when I need to override the default behavior of HtmlHelper :-(
然而,在他们有限的预算和无限的智慧中,C# 语言设计者只选择了更高效、更受限制的替代方案。当我需要覆盖 HtmlHelper 的默认行为时,这让我仍然是 SOL :-(
回答by ZXX
First it would be good to check [new] as a method modifier - it should give the same perf and method separation but with a lot less of a hassle.
首先,最好检查 [new] 作为方法修饰符 - 它应该提供相同的性能和方法分离,但麻烦要少得多。
If after that you are are still pondering the same tricks, here are the principal options to consider - no ideology involved, just listing apsects you'll need to be aware of :-)
如果在那之后你还在思考同样的技巧,这里是要考虑的主要选项 - 不涉及意识形态,只列出你需要注意的方面:-)
For a start you may have to limit all uses to templated function params in order to guarantee deterministic function selection since this trick will create the situation which is exact inverse of the one in which CLR guarantees deterministic binding priority across function call boundaries.
首先,您可能必须将所有用途限制为模板化函数参数,以保证确定性的函数选择,因为此技巧将创建与 CLR 保证跨函数调用边界的确定性绑定优先级的情况完全相反的情况。
If you are still going to have abstract base class and you own all code then just "converting" one base method into extension weill not buy you anything at all and will just cost you the loss of a common interface which will still exist for everything else, including the cost of vtables.
如果您仍然要拥有抽象基类并且您拥有所有代码,那么只需将一个基本方法“转换”为扩展,您根本不会购买任何东西,只会让您失去一个通用接口,该接口仍然存在于其他所有内容中,包括 vtables 的成本。
If you are aiming at converting abstract class into a concrete, eliminating all virtual methods for perf reasons and using using concrete base class and all derivatives exclusively in templates (maybe even converting to structs) then that trick could buy you some perf. You just need to be aware that you won't be able to go back to interface-based use without refactoring. You'll also need to test invocation order in non-template functions with the base class in signature and be extra carefull if you have multiple dlls-s. Try the same with [new] operator and see if it works better.
如果您的目标是将抽象类转换为具体类,出于性能原因消除所有虚拟方法,并在模板中专门使用具体基类和所有派生类(甚至可能转换为结构),那么该技巧可以为您带来一些性能。您只需要注意,如果不进行重构,您将无法返回到基于接口的使用。您还需要使用签名中的基类测试非模板函数中的调用顺序,如果您有多个 dlls-s,则要格外小心。用 [new] 运算符尝试相同的操作,看看它是否效果更好。
Also, you will have to write separate method implementation for each derived class even if they have exactly the same functionality, so if you have a lot of derived classes you are looking at a lot of code duplication. This part will hit you even if you use [new] unless you go back to virtual methods and introduce another layer in inheritance - which will cancel all perf gains of course.
此外,即使它们具有完全相同的功能,您也必须为每个派生类编写单独的方法实现,因此如果您有很多派生类,您就会看到很多代码重复。即使您使用 [new] 这部分也会影响您,除非您回到虚拟方法并在继承中引入另一层 - 这当然会取消所有性能增益。
回答by JoshRivers
Adding a link to another SO question that has an alternate answer than the "You can't". More 'you can't....unless'.
添加指向另一个 SO 问题的链接,该问题的答案不是“您不能”。更多'你不能......除非'。
回答by Josh C.
You may want to consider foregoing polymorphism if you don't need unique derivative classes.
如果您不需要唯一的派生类,您可能需要考虑上述多态性。
A few general guidelines from this MSDN article:
这篇 MSDN文章中的一些一般准则:
Once a method is defined within a class, any extension method for that class sharing that method name will no longer be executed.
Extension methods are loaded by namespace. To allow the user of other extension methods, you should probably avoid putting the extension methods in the same namespace as the class being extended.
在类中定义方法后,将不再执行该类的任何共享该方法名称的扩展方法。
扩展方法由命名空间加载。为了允许其他扩展方法的用户,您可能应该避免将扩展方法放在与被扩展的类相同的命名空间中。
If various dependent assemblies are consuming the same library, each requiring a customized function against a class in the library, you could declare the extension specific to the dependent assembly in a namespace within that assembly. Other assemblies which don't depend on that assembly won't have that extension.
如果各种依赖程序集使用同一个库,每个程序集都需要针对库中类的自定义函数,您可以在该程序集中的命名空间中声明特定于依赖程序集的扩展。不依赖于该程序集的其他程序集将没有该扩展名。
While this is not polymorphism, you may not want to use polymorphism as it would require all derived classes to implement their overrides.
虽然这不是多态性,但您可能不想使用多态性,因为它需要所有派生类实现它们的覆盖。
In other words, if you have derivatives with specific, variant logic, use polymorphism. If you are simply needing custom functions in specific cases that otherwise might be confusing or burdensome, extension methods are not a bad idea.
换句话说,如果您有具有特定变体逻辑的衍生物,请使用多态。如果您只是在特定情况下需要自定义函数,否则可能会令人困惑或繁琐,扩展方法不是一个坏主意。
Also, see JoshRivers' answer showing how to override an extension method using a namespace within a class within a separate namespace.
另外,请参阅 JoshRivers 的回答,其中显示了如何使用单独命名空间内的类中的命名空间来覆盖扩展方法。