3

I understand we can use partial classes to separate the implementations of interfaces so it is more organized and more readable.

However, what if the interface I am implementing inherits other interfaces such as below:

public interface IA
{
    void DoA();
}
public interface IB : IA
{
    void DoB();
}
public partial class B : IB
{
    public void DoB() { }
}
public partial class B : IA
{
    public void DoA() { }
}

That would be the same as doing this:

public class B : IB, IA { // ... }

The compiler allows that but I am not sure if that will get me in trouble. Can someone please clarify if it will cause issues.

I am thinking of doing this instead and use a comment but I rather go with the above approach if it will cause no issues:

public partial class B : IB
{
    public void DoB() { }
}
public partial class B // IA Implementation
{
    public void DoA() { }
}

Edit

If I do below compiler will complain that "'IA' is already listed in interface list.":

public class B : IA, IA
{
}

but it will allow this:

public class B : IB, IA { // ... }

Why would it allow it if IB inherits IA. Clearly the 2 scenarios are different since the compiler allows one and not the other. There must be a reason.

CodingYoshi
  • 25,467
  • 4
  • 62
  • 64
  • 3
    _"please clarify if it will cause issues"_ Like what? I think the compiler messages are pretty clear. If you implement `IA` in either file but don't include the implementation of `DoA()`, the compiler will complain. If, on the other hand, you add the implementation of `DoA()` on either side, the compiler stops complaining. Partial classes act as one unit so it doesn't matter where you use `: IA` and where you actually implement its methods; it's the same class after all. – 41686d6564 stands w. Palestine Oct 26 '19 at 22:18
  • @AhmedAbdelhameed No the compiler will not complain if you do what I have shown. That's the whole point of posting this question. I know what partial classes are and that they act as one unit. In other words it would be like this: `public class B : IB, IA`. – CodingYoshi Oct 26 '19 at 22:21
  • I never said the compiler will complain about the code you have in the question. Please read my comment again. _"I know what partial classes are and that they act as one unit"_ What exactly is your question then? What kind of issues do you think you might run into? – 41686d6564 stands w. Palestine Oct 26 '19 at 22:25
  • @AhmedAbdelhameed My question is would this cause any issues? I don't know what issues, thus I am asking. – CodingYoshi Oct 26 '19 at 22:28
  • In your last edit, you included the equivalent code when using "one class"; that's great! Now, if you _know_ a) They're equivalent. b) That the second code (no partial) works fine... why would you think that the first code (which is equivalent to it) may cause issues? Unless you're not sure whether a and/or b are correct, in which case, you probably should clarify that in your question. – 41686d6564 stands w. Palestine Oct 26 '19 at 22:32
  • @AhmedAbdelhameed See my last edit. – CodingYoshi Oct 26 '19 at 22:37
  • Ah, I see your point now. I still don't think it'll cause any issues. You shouldn't take my word for it though. I'll let you know if I find some evidence to back this up. – 41686d6564 stands w. Palestine Oct 26 '19 at 22:51
  • I found [this post](https://stackoverflow.com/q/47209963/8967612). The question is a little bit different but the concept is the same (i.e., implementing an interface through two different paths). Might be useful. P.S. Check both answers there. – 41686d6564 stands w. Palestine Oct 26 '19 at 23:01
  • 2
    C# programmers have to unlearn the term "inheritance" when talking about interfaces. It is like inheriting uncle Harry's estate and having to pay off his gambling debts. All that deriving an interface does is demand you to implement the base interface methods as well. You do not have to list IA in the B declaration, IB already demands that you implement the IA members. The C# compiler allows it however, it is self-documenting. – Hans Passant Oct 27 '19 at 08:18
  • If your B class implements the IB interface, it's already implementing the IA interface, so there really is not much point of writing `class B : IB, IA` except for readability. – Zohar Peled Oct 27 '19 at 08:29
  • And as for your question, I would say that partial classes are not to be used for readability. In fact, I would only use the `partial` keyword to divide a class to two separate files if one part of it is partially auto generated - like a winforms form, or something like this. If you want to show in your code what method implements what interface, use regions. – Zohar Peled Oct 27 '19 at 08:33
  • @hanspassant I agree but not totally. If an interface inherits another interface, it is more appropriate to say "inherits". However if a class inherits an interface, it's more appropriate to say "implements" and I have used such wording. – CodingYoshi Oct 27 '19 at 09:53
  • Probably because my C++ background, but I always think that if you need partial class for readability then you need to refactor for making classes that are readable in a single file. – Jepessen Oct 27 '19 at 16:22
  • @Jepessen No, this is a common practice in C#. This has nothing to do with refactoring. It just clearly shows that this `partial` part is for this interface. – CodingYoshi Oct 27 '19 at 16:24
  • @Jepessen It is a preference thing as shown [here](https://stackoverflow.com/questions/4681179/implementing-interfaces-in-partial-classes). – CodingYoshi Oct 27 '19 at 16:31
  • 2
    Unfortunately the C# specification uses the term "inheritance" when describing the contract "an implementer of this interface must also implement that interface"; I agree that this is not the best choice of terminology. Something that helps is to realize that "inheritance" simply means "the inheritable members of this type are also members of that type". It does not imply implementation sharing, rather, just that there is a subsetting relationship between one set of members and another. – Eric Lippert Oct 27 '19 at 16:51

3 Answers3

9

The compiler allows that but I am not sure if that will get me in trouble. Can someone please clarify if it will cause issues.

Most of the time this is a reasonable practice. I often use partial classes to separate out an interface implementation, or the implementation of a nested class, and you should feel confident doing so.

However, there is one situation where you can be surprised by the practice you describe of explicitly calling out an implemented interface, and that is the interface re-implementation rule.

It is best described with a little example. Suppose you have

interface I { void Foo(); }
// B does not implement I.
class B { public void Foo() {} }
class C : B, I { }

That's perfectly legal. C implements I, which means C must have method Foo, and C does have method Foo; it inherited it from B.

Now consider:

// D implements I because D derives from C
class D : C { public new void Foo() {} }
// The I is unnecessary here, right? Or is it?
class E : D, I { }

The interface re-implementation rule is: because E explicitly says that it implements I, the compiler re-does the analysis to determine which method Foo matches I.Foo. Summing up:

  • ((I)(new C())).Foo() calls B.Foo
  • ((I)(new D())).Foo() calls B.Foo
  • ((I)(new E())).Foo() calls D.Foo

This can be surprising, and it can happen unexpectedly when you unnecessarily state an interface. It almost never happens, and it almost never matters when it does, but you asked for any possible issue, and this is such a situation.

Now let's consider your follow-up question:

Clearly the two scenarios are different since the compiler allows one and not the other. There must be a reason.

As we've already seen, C#'s designers wanted a way to express "I've introduced a new member that I want to be bound to the interface members", and the somewhat obscure choice they made was to allow this redundant declaration to mean "do the re-binding now".

However, there is another design reason to allow that sort of redundancy, and moreover, to not warn about it when it happens.

A good practice to get into when thinking about odd C# design decisions is to remember that the designers of C# are explicitly worried at all times about stuff changing in versioned software. In particular, they worry about many different forms of "brittle base class failure". That is the failure mode where someone makes a change that is perfectly reasonable to a base class, which then causes a derived class to surprisingly break or behave strangely.

Consider for example this sequence of events. First, developer X writes:

interface IA { void Foo(); }

and then developer Y writes

class C { public void Foo() {} }

and then developer Z writes:

class D : C, IA { }

and then developer Y realizes, oh, I implemented the contract of IA, I can get that for free, and changes class C to

class C : IA { }

So now the question is: should the compiler give a warning on class D for being redundant? And the answer is no, the author of class D did nothing wrong, and should not be made to do work to fix a problem that doesn't actually exist.

That's why C# tends to be pretty tolerant of redundancy, though unfortunately it is not always consistently tolerant. (Re-statements of generic constraints, for example, are illegal but in my opinion should be allowed.)

In short, any time you think "C# could be telling me about this redundancy" ask yourself what if it was caused by someone else making a change that I don't control? Would that be annoying or helpful? If annoying, the language design probably suppresses the warning deliberately.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • This is great and this is what I was after. You probably knew this but I arrived at it after hours of thinking about it and then came across a re-implementation article somewhere. Glad we both have the same conclusion, although you did point out the re-implementation example differently than in my answer. – CodingYoshi Oct 27 '19 at 17:05
  • @CodingYoshi: Glad to help. There is another design issue as well to consider; I'll add some extra text to my answer. – Eric Lippert Oct 27 '19 at 17:06
0

There are no issues. Partial classes are for your convenience. They don’t affect the generated code. If you can write a non-partial class, you can then split it into an arbitrary number of partial classes with no change in behavior.

The compiler doesn’t let you list the exact same interface twice in the list of implemented interfaces because that would be silly and usually would imply that you made a mistake (meant one thing but wrote another). But this behavior is only to help you not make mistakes, and thus the compiler only verifies that the interfaces don’t literally have same names in the list. If that check passes, the compiler then treats the implemented interfaces as a set of unique elements, no matter how many times an interface is added via different inheritance paths.

Suppose you have IA extending IDisposable and IB extending IDisposable, and your class implements both IA and IB. The compiler sees your class as implementing IA, IB and IDisposable as if they were themselves not extending any other interfaces.

Do note that C# is not C++, and the latter don’t have interfaces. In C++ code, the equivalent of C#’s class inheritance and interface implementation would be approximated with virtual inheritance. But modern Pascal, as well as CoDeSys-3 extensions of IEC-61131-3 provide interfaces with semantics similar to those in C#.

Hans’s admonition to stop calling interface implementation “inheritance” is to be ignored at your own peril. One is inheriting nothing when implementing an interface. The interface has no data, no implementation, it’s just a declaration of what methods and properties the implementing classes need to provide. The interfaces are essentially contracts between the user of the class and the author of the class. Inheritance is more than that. Inheritance is concrete, it means that you take a class type and add more stuff to it. Interface implementation is nothing like that: you don’t have any stuff, you just declare that here’s a list of what methods and properties are implemented, and such lists are like contracts: a method taking an “interface” type as input is not really asking for the interface. It’s asking for some concrete object that honors the interface’s contract.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
0

I thought more about this and came to the conclusion, with confidence, that it will not cause any issues. For example, for brevity if we were to remove the concept of partial for a bit so we can concentrate on the issue at hand, if we did the following:

public class B : IB, IA
{
    public void DoA() { }
    public void DoB() { }
}

The C# compiler will not complain because even though we are unnecessarily indicating that B implements IA, it is still true. We are repeating ourselves but it is still true. Perhaps the compiler should still warn us that 'IA' is already listed in interface list or some other warning, but it does not.

Thus repeating the fact that the class implements the interface again, changes nothing and will cause no issues.

Having said that, we have to be careful if we do this in a derived class.


The compiler will not allow below and complain it hides an inherited member and if hiding is meant, then to use the new keyword.

public class C : B, IA
{
    // Compiler will not allow it without new keyword
    void DoA() { Console.WriteLine("DoA in C."); }
}

However, and this is the part we have to be careful about, if we explicitly implement the interface such as below:

public class C : B, IA
{
    void IA.DoA() { Console.WriteLine("DoA in C."); }
}

It will allow it and now we have overridden the functionality specified in B.

Here is the complete code to demonstrate what I am talking about.

public interface IA
{
    void DoA();
}
public interface IB : IA
{
    void DoB();
}
public class B : IB, IA
{
    public void DoA() { Console.WriteLine("DoA in B."); }
    public void DoB() { Console.WriteLine("DoB in B."); }
}

public class C : B, IA
{
    void IA.DoA() { Console.WriteLine("DoA in C."); }
}

And a quick test:

var b = new B();
b.DoA();
b.DoB();

var c = new C();
c.DoA();           // Prints: DoA in B.
(c as IA).DoA();   // Prints: DoA in C.

Fiddle Me

CodingYoshi
  • 25,467
  • 4
  • 62
  • 64