问题
I've checked through other questions and surprisingly this question doesn't seem to have been asked. With Extension methods, interfaces provide limited but true implementation multiple inheritance. This brings with it the Diamond problem, the same as with class based multiple inheritance. Why is this better or more acceptable than class based multiple inheritance that so many people seem to find so horrifying? It actually seems a much worse way of implementing multiple inheritance as extension methods can't go in the interface itself or even a class that implements the interface but can end up scattered over multiple static utility classes.
Eric Lippert in his blog (5 Oct 2009 9:29 AM) seemed open to the idea of extension properties and even mentions the possibility of extension events, extension operators, extension constructors (also known as "the factory pattern"). So implementation through interfaces could be further extended.
Edit: To clarify if a class inherits from two interfaces that both have an extension method of the same name and type parameters then it will produce a compile error if a method is called with out explicitly naming the interface. Having thought about this I was mistaken as this is not the Diamond problem. However thinking about this raises the question of what is so important about the Diamond problem as opposed to other ambiguities? Why is the Diamond problem such a difficulty, that it can it not be picked up with a simple compile error the same as when interface extension methods class clash and are not implicitly resolvable? Even within class based multiple inheritance it is possible to have member signature clashes that are not Diamond based.
回答1:
With extension methods, interfaces provide limited but true implementation multiple inheritance.
This sentence is the basis for the entire question but I cannot make heads nor tails of it, so it will be difficult to answer the question.
First off, let's clearly define "inheritance". When we say that a type D inherits from a type B, what we mean is that every member of B is also a member of D†. That is all that we mean by "inheritance" in C#.
A class (or struct) inherits members from exactly one(††) base class. A class may implement any number of interfaces; this is quite different from base class inheritance. A class is not required to have the same set of members that an interface it implements has, because the class can use explicit interface implementation to provide an implementation without making a member of the class. The explicit interface implementation is only accessible via the interface, and cannot be accessed in any other way, so it would be strange to think of it as a "member" of the class that is "inherited" from the interface.
An interface "inherits" members from any number of other interfaces. And technically, this can be thought of as inheritance; members of the base interfaces are members of the derived interface. But I wish that we had not described it like that in the specification; I think it would have been more clear to say that interfaces do not inherit from base interfaces; rather, an interface can require the implementation of other interfaces as part of its contract.
Now that we've got that out of the way, what about extension methods? Extension methods are not any kind of inheritance; the type that is extended does not get any new members. Extension methods are rather just a way to more pleasantly write a call to a static method.
This brings with it the Diamond problem, the same as with class based multiple inheritance
It is unclear what "this" refers to in that sentence. Are you referring to (1) classes implementing multiple interfaces, (2) interfaces inheriting from multiple interfaces, or (3) something about extension methods, or (4) something else entirely? I do not understand what the diamond problem has to do with your question. Can you clarify it?
Why is this better or more acceptable than class based multiple inheritance that so many people seem to find so horrifying?
Why is what better?
I'm not understanding this question at all but it seems like there is some kind of useful question in here somewhere. Can you clarify the question? Preferably with some short, simple example code that demonstrates what you're talking about.
† Not every member. Constructors and destructors for example are members, but are not inheritable members. Private members are inherited but might not be accessible by name.
†† Except for object
, which inherits from zero classes. Every other class inherits from exactly one class.
回答2:
Any appearance of extension methods implementing multiple inheritance is wholly an illusion. They do not.
Extension methods are faily simple compiler tricks. They compile to plain old static methods that look and work just as they would with the this
removed from the first parameter.
Consider:
myObj.Extension();
...
public static class MyExtension
{
public static void Extension(this MyObj myobj)
Calling the extension is equivalent to this:
MyExtension.Extension(myObj);
You can even call it like that in your code, of course.
回答3:
The list of interfaces a C# class implements is flattened, so when a class implements an interface by virtue of inheriting it through multiple interfaces that it implements, the number of implementations the class needs to provide remains one.
For example, if a class implements two interfaces both of which inherit from IDisposable
, that class still needs to implement Dispose()
only once. This is in contrast to C++, where functions that are inherited from the same base class through multiple paths of non-virtual inheritance need to be overriden separately.
Extension methods are orthogonal to this issue, because the implementations they provide cannot be overriden. I wrote a blog post on extension methods and their role in sharing implementation "horizontally". I view them as a mechanism of providing functionality in a way entirely independent of the "vertical" implementation sharing that you get through class inheritance.
回答4:
Extension methods are just glorified static methods that look like instance methods at call time (if the caller so chooses).
The situation you're describing cannot happen because the compiler will flag the call as ambiguous:
interface I1 { }
interface I2 { }
class C : I1, I2 { }
static class Ex1 {
public static void M(this I1 self) { }
}
static class Ex2 {
public static void M(this I2 self) { }
}
...
new C().M(); // ERROR: The call is ambiguous
Extension methods are only in effect if you import the namespace containing the static class with the extension method in the current context (via a using
directive); or if your declarations live in the same namespace as them. So, even though you can create ambiguous declarations, if you add them to different namespaces, then the caller can disambiguate by only importing the desired namespace.
Also, to disambiguate, the caller can call the method as a normal static method:
Ex1.M(new C()); // not ambiguous anymore
Or you can cast to the appropriate interface:
((I1)new C()).M(); // not ambiguous anymore
So, it's not as if you're "inheriting" conflicting members that must be resolved at declaration time, you have both at your disposal, and you have to tell the compiler which one you want to use at call time.
Side note: I find this ability to extend interfaces an interesting way to create a form of mixin in C#. I've written about it before, e.g., here.
来源:https://stackoverflow.com/questions/9983981/why-does-c-sharp-allow-multiple-inheritance-though-interface-extension-methods-b