Explicit Implementation of Marker C# Interfaces - c#

Imagine a situation where two marker interfaces A and B guard single Base interface:
interface Base
{
void Hello();
}
interface A: Base
{
}
interface B: Base
{
}
Is there a way to define class C that implements both A and B while providing marker interface specific Hello implementations? Note that Base, A and B interfaces are declared in an external library so I could not modify it.
If I do this
class C: A, B
{
void A.Hello() =>
Console.Out.WriteLine("A");
void B.Hello() =>
Console.Out.WriteLine("B");
}
...it results in CS0539 compiler error:
member in explicit interface declaration is not a member of interface.

No, it's not possible in C# without modifying the interfaces. The only explicit interface implementation that is possible is Base.Hello:
class C: A, B
{
void Base.Hello() =>
Console.Out.WriteLine("Base");
}

Related

Abstract modifier on interface method

Today I was copying some methods from an abstract class to an Interface and I realized that the compiler does not underline the abstract keyword. I tried to look up the documentation but found nothing about it.
I also put it into SharpLab but see no difference between the two.
public interface ITestAbstract
{
public abstract void MyTest();
}
public interface ITest
{
public void MyTest();
}
My guess is, that it is allowed since, by default interface methods are actually abstract methods, or am I missing something out?
This feature was added in C# 8 - Default Interface Methods:
Modifiers in interfaces
The syntax for an interface is relaxed to permit modifiers on its members. The following are permitted: private, protected, internal, public, virtual, abstract, sealed, static, extern, and partial.
This means that you are not allowed to modify your methods with abstract before this.
One of the purposes is to support reabstraction. Example from the docs:
interface IA
{
void M() { WriteLine("IA.M"); }
}
interface IB : IA
{
abstract void IA.M();
}
class C : IB { } // error: class 'C' does not implement 'IA.M'.

Explicitly marking derived class as implementing interface of base class

interface IBase
{
string Name { get; }
}
class Base : IBase
{
public Base() => this.Name = "Base";
public string Name { get; }
}
class Derived : Base//, IBase
{
public Derived() => this.Name = "Derived";
public new string Name { get; }
}
class Program
{
static void Main(string[] args)
{
IBase o = new Derived();
Console.WriteLine(o.Name);
}
}
In this case output will be "Base".
If I explicitly state that Derived implements IBase (which is in fact already implemented by base class Base and such annotation seem to be useless) the output will be "Derived"
class Derived : Base, IBase
{
public Derived() => this.Name = "Derived";
public new string Name { get; }
}
What's the reason for such behavior?
VS 15.3.5, C# 7
It's explained in sections 13.4.4 to 13.4.6 of the C# 5 specification. The relevant sections are quoted below, but basically if you explicitly state that a class implements an interface, that triggers interface mapping again, so the compiler takes that class as the one to use to work out which implementation each interface member is mapped to.
13.4.4 Interface mapping
A class or struct must provide implementations of all members of the interfaces that are listed in the base class list of the class or struct. The process of locating implementations of interface members in an implementing class or struct is known as interface mapping.
Interface mapping for a class or struct C locates an implementation for each member of each interface specified in the base class list of C. The implementation of a particular interface member I.M, where I is the interface in which the member M is declared, is determined by examining each class or struct S, starting with C and repeating for each successive base class of C, until a match is located:
If S contains a declaration of an explicit interface member implementation that matches I and M, then this member is the implementation of I.M.
Otherwise, if S contains a declaration of a non-static public member that matches M, then this member is the implementation of I.M. If more than one member matches, it is unspecified which member is the implementation of I.M. This situation can only occur if S is a constructed type where the two members as declared in the generic type have different signatures, but the type arguments make their signatures identical.
...
13.4.5 Interface implementation inheritance
A class inherits all interface implementations provided by its base classes.
Without explicitly re-implementing an interface, a derived class cannot in any way alter the interface mappings it inherits from its base classes. For example, in the declarations
interface IControl
{
void Paint();
}
class Control: IControl
{
public void Paint() {...}
}
class TextBox: Control
{
new public void Paint() {...}
}
the Paint method in TextBox hides the Paint method in Control, but it does not alter the mapping of Control.Paint onto IControl.Paint, and calls to Paint through class instances and interface instances will have the following effects
Control c = new Control();
TextBox t = new TextBox();
IControl ic = c;
IControl it = t;
c.Paint(); // invokes Control.Paint();
t.Paint(); // invokes TextBox.Paint();
ic.Paint(); // invokes Control.Paint();
it.Paint(); // invokes Control.Paint();
...
13.4.6 Interface reimplementation
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. For example, in the declarations
interface IControl
{
void Paint();
}
class Control: IControl
{
void IControl.Paint() {...}
}
class MyControl: Control, IControl
{
public void Paint() {}
}
the fact that Control maps IControl.Paint onto Control.IControl.Paint doesn’t affect the re-implementation in MyControl, which maps IControl.Paint onto MyControl.Paint.
If Derived does not implement IBase and declares new string Name, that means that Derived.Name and IBase.Name are logically not the same. So when you access IBase.Name, it looks for it in Base class, implementing IBase. If you remove new string Name property, the output will be Derived, because now Derived.Name = Base.Name = IBase.Name. If you implement IBase excplicitly, the output will be Derived, because now Derived.Name = IBase.Name. If you cast o to Derived, the output will be Derived, because now you are accessing Derived.Name instead of IBase.Name.

access same-name members when class C : A : B

I made a bad mistake in trying to design a framework where one class would inherit two others; forgetting that C# doesn't have multiple inheritance. Because of this error, I made a workaround.
A : B
Both : A //Now has all members of both A and B, unless they're hidden
If A and B both have a member by the same name, can I access the member of B (hidden by A) without changing the framework, and if so, how?
Edit: Classes A and B are not meant to be instantiated.
One approach you could take is to extract the method into a pair of interfaces and use explicit interface implementations in classes A, B.
interface IA {
void Method();
}
interface IB {
void Method();
}
abstract class B : IB {
void IB.Method() { ... }
}
abstract class A : B, IA {
void IA.Method() { ... }
}
class Both : A { ... }
You must then cast to the interface to access the methods, and attempting to call Method without the cast results in a compile time error.
Both x = new Both();
((IA)x).Method();
((IB)x).Method();
x.Method() //invalid call
C c = new C();
c.Member("This is C's version, if there is one; otherwise it's A's");
((A)c).Member("This is A's version");
((B)c).Member("This is B's version");

How to make some classes implement an interface while these classes are built into assembly

What I mean is that for example, there are class A, class B and class C. A, B, C all have a method void M(), but none of them are declared implemented any interface.
So I give an interface with a void M() constraint. But the class A, B, C are types in a assembly.
Is there any way to use those class as if they are already implemented an interface.
No, you can't modify existing classes this way.
You can derive new classes and say they implement new interface. Existing methods in base class will be picked as interface implementation.
interface IWithM
{
void M();
}
class MyA : A, IWithM
{
// IWithM.M will be picked from A.M
}
No, there is no way to do that.
You could, however, write adapter classes that inherit from A, B and C and implement the interface.
public class AAdapter : A, IMyInterface
{
}
With the decorator pattern.
You could create your own version of these classes and then decorate the implementation, without modifing the assembly owned by someone else.
Your interface:
public interface IImplementsM()
{
void M();
}
public class MyA : IImplementsM
{
private A _a;
public MyA(A a){
_a = a;
}
public void M(){
_a.M();
}
}
Then in your code instead of using A you can use your own version or the Interface.
// some other class
public void DoSomething(IImplementsM implementsM)
{
implementsM.M();
}
And to execute could be something like:
var myA = new MyA(new A());
DoSomething(myA);
Advantage, you can now have a contraint when working in your own code.
Distadvantage, you have to then use your version of the class in your assembly, not theirs.

C#: Property overriding by specifying the interface explicitly

While attempting to override the explicit interface implementation of the ICollection<T>.IsReadOnly property from the Collection<T> class, I came across some documents stating that explicit interface member implementations cannot be overridden because they cannot have modifiers such as virtual or abstract. On MSDN they even go as far as specifying how to make an explicit interface member implementation available for inheritance by creating another abstract or virtual member which is called by the explicit interface member implementation. No problems so far.
But then I wonder: Why is it possible in C# to override any explicitly implemented interface member just by specifying the interface explicitly?
For example, suppose I have a simple interface like this, with a property and method:
public interface IMyInterface
{
bool AlwaysFalse { get; }
bool IsTrue(bool value);
}
And a class A which implements the interface explicitly, and has a method Test() which calls its own interface member implementation.
public class A : IMyInterface
{
bool IMyInterface.AlwaysFalse
{ get { return false; } }
bool IMyInterface.IsTrue(bool value)
{ return value; }
public bool Test()
{ return ((IMyInterface)this).AlwaysFalse; }
}
As you can see, none of the four members are virtual or abstract, so when I define a class B like this:
public class B : A
{
public bool AlwaysFalse
{ get { return true; } }
public bool IsTrue(bool value)
{ return !value; }
}
Then you'd expect an instance of B cast to A to behave like A. And it does:
A a = new A();
Console.WriteLine(((IMyInterface)a).AlwaysFalse); // False
Console.WriteLine(((IMyInterface)a).IsTrue(false)); // False
Console.WriteLine(a.Test()); // False
A b = new B();
Console.WriteLine(((IMyInterface)b).AlwaysFalse); // False
Console.WriteLine(((IMyInterface)b).IsTrue(false)); // False
Console.WriteLine(b.Test()); // False
Now comes the catch. Create a class C which is an exact copy of B except for one thing in the class declaration:
public class C : A, IMyInterface
{ /* ... same as B ... */ }
Now an instance of C, when cast to A, doesn't behave like A but like C:
A c = new C();
Console.WriteLine(((IMyInterface)c).AlwaysFalse); // True
Console.WriteLine(((IMyInterface)c).IsTrue(false)); // True
Console.WriteLine(c.Test()); // True
Even the Test() method now calls the overridden method in C! Why is this?
This has nothing to do with explicit interface implementation; it's simply a consequence of the general rules of inheritance and interface mapping: you would see exactly the same results if type A provided an implicit, rather than explicit, implementation of IMyInterface.
Type B inherits from type A. Nothing is overridden.
B provides its own AlwaysFalse and IsTrue members but they don't implement IMyInterface; The implementation of IMyInterface is provided by the members inherited from A: when an instance of type B is cast to IMyInterface then it behaves in exactly the same way as an instance of type A because A is providing the members that implement the interface.
Type C inherits from type A. Again, nothing is overridden.
C provides its own AlwaysFalse and IsTrue members but this time those members do implement IMyInterface: when an instance of type C is cast to IMyInterface then the members of C provide the interface implementation rather than those of A.
Because type A implements IMyInterface explicitly the compiler doesn't warn that the members of B and C are hiding the members of A; In effect those members of A were already hidden due to the explicit interface implementation.
If you changed type A to implement IMyInterface implicitly rather than explicitly then the compiler would warn that the members of B and C are hiding, not overriding, the members of A and that you should ideally use the new modifier when declaring those members in B and C.
Here are some relevant bits from the language specification. (Sections 20.4.2 and 20.4.4 in the ECMA-334 spec; sections 13.4.4 and 13.4.6 in the Microsoft C#4 spec.)
20.4.2 Interface mapping
The implementation of a particular
interface member I.M, where I is
the interface in which the member M
is declared, is determined by
examining each class or struct S,
starting with C and repeating for
each successive base class of C,
until a match is located.
20.4.4 Interface re-implementation
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.

Categories

Resources