Can someone please help me understand why this code snippet returns "Bar-Bar-Quux"? I'm having a hard time understanding this even after reading up on interfaces.
interface IFoo { string GetName(); } class Bar : IFoo { public string GetName() { return "Bar"; } } class Baz : Bar { public new string GetName() { return "Baz"; } } class Quux : Bar, IFoo { public new string GetName() { return "Quux"; } } class Program { static void Main() { Bar f1 = new Baz(); IFoo f2 = new Baz(); IFoo f3 = new Quux(); Console.WriteLine(f1.GetName() + "-" + f2.GetName() + "-" + f3.GetName()); } }
It was mainly developed as a system programming language to write an operating system. The main features of the C language include low-level memory access, a simple set of keywords, and a clean style, these features make C language suitable for system programmings like an operating system or compiler development.
Operation: The -> operator in C or C++ gives the value held by variable_name to structure or union variable pointer_name. Difference between Dot(.) and Arrow(->) operator: The Dot(.) operator is used to normally access members of a structure or union.
In C programming language, %d and %i are format specifiers as where %d specifies the type of variable as decimal and %i specifies the type as integer. In usage terms, there is no difference in printf() function output while printing a number using %d or %i but using scanf the difference occurs.
There are two things happening here. One is member hiding. This is fairly well-known and covered elsewhere. The other, less-known feature is interface re-implementation covered in section 13.4.6 of the C# 5 specification. To quote:
A class that inherits an interface implementation is permitted to re-implement the interface by including it in the base class list. A re-implementation of an interface follows exactly the same interface mapping rules as an initial implementation of an interface. Thus, the inherited interface mapping has no effect whatsoever on the interface mapping established for the re-implementation of the interface.
and
Inherited public member declarations and inherited explicit interface member declarations participate in the interface mapping process for re-implemented interfaces.
The result for f1.GetName()
is "Bar" because the method Baz.GetName
is hiding Bar.GetName
and f1
is declared as type Bar
. There is no dispatch to the run-time type's implementation unless it is explicitly declared as virtual and overridden.
Similarly, for f2.GetName()
, Baz.GetName
is hiding the implementation in Bar
, so it is not called when using dispatch through a reference to the interface. The interface is "mapped" to the method declared in Bar
because that is the type on which the interface was declared. It does not matter that Baz
has a compatible method with the same name. The rules for interface mapping are defined in section 13.4.4 of the spec. If GetName
had been declared virtual in Bar
, it could be overridden, which then would be called through the interface. The result is therefore also "Bar".
For f3.GetName()
, Quux
re-implements IFoo
so it gets to define its own mapping to GetName
. Note that it also hides the implementation inherited from Bar
. It is not necessary to use new to do the re-implementation, it simply suppresses the warning about hiding. Therefore the result is "Quux".
So that explains the output that you see: "Bar-Bar-Quux"
This post by Eric Lippert discuss some more nuances in this tricky feature.
Interfaces by definition have no associated implementation, which is to say their methods are always virtual and abstract. In contrast, the class Bar
above defines a concrete implementation for GetName
. This satisfies the contract required to implement IFoo
.
Class Baz
now inherits from Bar
and declares a new
method GetName
. That is to say that the parent class Bar
has a method with the same name, but it is completely ignored when working with Baz
objects explicitly.
However, if a Baz
object is cast as a Bar
, or simply assigned to a variable of type Bar
or IFoo
, it will do as it's told and behave like a Bar
. In other words, the method name GetName
refers to Bar.GetName
instead of Baz.GetName
.
Now, in the third case, Quux
both inherits from Bar
and implements IFoo
. Now, when cast as an IFoo
it will provide its own implementation (according to the specification provided in Mike Z's answer).
When a Quux is cast as a Bar, however, it returns "Bar", just as Baz does.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With