Why is it illegal to have a private setter on an explicit getter-only interface implementation?
Asked Answered
D

5

55

I tend to favor explicit interface implementations over implicit ones, as I think programming against the interface as opposed to against an implementation, is generally preferable, plus when dealing with web-services it is often a necessity.

That said, I was wondering why the following is illegal with an explicit interface declaration and legal with an implicit one:

interface IConnection
{
    string ConnectionString { get; }
}

class Connection1 : IConnection
{
    // private set is illegal, won't compile
    string IConnection.ConnectionString { get; private set; }
}

class Connection2 : IConnection
{
    // private set is legal now, it is not part of the interface
    string ConnectionString { get; private set; }
}

I know how to fix this, as it is legal to have both an explicit and implicit interface, plus I can make the implicit interface implementation completely private.

Yet I am wondering about the reasoning behind this. Because technically, the internally compiled private method set_IConnection_ConnectionString does not need to be part of the interface, right? It could just be seen as an auxiliary setter, not part of the interface, as it is in the implicit implementation situation.

Update: as a bonus, the seemingly confusing, and in my opinion not quite right compile error you receive is the following:

The accessibility modifier of the accessor must be more restrictive than the property Connection1.ConnectionString

Excuse me, more restrictive than private, how... what?

Dunnite answered 12/5, 2014 at 14:36 Comment(13)
Sounds like a question specifically for Eric Lippert :-) My take would be that it is a bug in the compiler.Bigot
@Dunnite I'd be curious to know if this is still an issue using Roslyn.Mountbatten
@AdamHouldsworth, I haven't tried it with Roslyn, but I doubt it will change compiletime behavior that has been around so long. Unless it is a bug, as dasblinkenlight suggests.Dunnite
Pretending things that aren't part of an interface are part of an interface would be a bug. Rejecting them is certainly not.Wiper
@Dunnite because there is no IConnection.ConnectionString_set.Agrimony
It does not compile since you would never be able to use it.Killifish
Downvotes? Why is this a bad question? Please comment if you downvote, that allows me to improve the question.Dunnite
Good question. The accepted answer is correct, but you are also entirely justified to point out that this error message is terrible. I'll pass that feedback along to the compiler team.Diva
@EricLippert: thanks for jumping in (kinda hoped you'd stop by ;). I should emphasize that there are two error messages: one for the settor and one for the private keyword, but the latter being really weird and pointing the developer in the wrong direction.Dunnite
I agree. Are you using the Roslyn preview? It seems to repro the bug on my machine. C# 3.0 by contrast gives a more sensible "private not allowed" error.Diva
@EricLippert: I got the error in VS2012/C# 4.0 and now tried it with VS2010/12 on .NET 2.0/C# 3.0. Got the same error on the private modifier as mentioned in my question. I didn't try Roslyn yet (not installed on this dev machine). Still, even "private not allowed" implies that something else is allowed, but it isn't (but I haven't come across that one using the above example). As an aside, notice that the error mentions Connection1.ConnectionString, which seems to refer to a non-explicitly implemented property.Dunnite
@Abel: Thanks; I've send some feedback to the team based on your description. Hopefully this can get fixed for the Roslyn final release.Diva
I get the perfectly understandable errors "The modifier 'private' is not valid for this item" followed by "'UserQuery.Connection1.UserQuery.IConnection.ConnectionString.set' adds an accessor not found in interface member 'UserQuery.IConnection.ConnectionString'"Dorice
C
66

The only way to invoke an explicit interface member is to cast the object to the correct interface and then invoke the member on that interface. But once you've cast to IConnection, the IConnection.ConnectionString has no setter.

So there's no way to invoke this private setter method.

Cordey answered 12/5, 2014 at 14:44 Comment(8)
(Outside of reflection)Misfortune
That seems to make sense, though I would argue that in the private atmosphere of the class itself, this could be made available. But I agree: even within the class, you will have to explicitly cast to the interface.Dunnite
@Dunnite - and now you have the issue that Eric Lippert talks about when talking about things like Infoof - now you have to invent some new syntax specifically to allow you to write this call. What if you have two explicit interface implementations both called ConnectionString - what syntax do you use to indicate which one you wish to use for any particular invocation?Cordey
Yes, exactly. I mean, that syntax isn't there, but if I put my syntax-writing hat on, I would come up with this.IInterfaceName.SomeMethod, which would then be the syntax available everywhere (yay! no annoying explicit interface casting needed anymore!) and it would resolve to private gettors/settors locally in the class. Hmm... still ugly though: you would access something on the interface that is not on the interface. Ok, my syntax-hat is not quite working yet.Dunnite
@Dunnite If your constantly trying to access explicitly implemented interface members on the concrete type, and doing so is annoying, it's a pretty strong sign that you should be implicitly implementing the members, not explicitly implementing them. I see no need for explicit implementation in this example.Misfortune
@Servy: I agree, but we're digressing. My "personal" need is that if I do not explicitly implement an interface, and I change the interface by moving a member, I may not find errors must later in the development process. But that doesn't mean I'll always go the extra mile and (also) implement the explicit interface.Dunnite
@Misfortune - re: reflection, agreed. But can you think of any other scenario where you're allowed to write as much code as you like and yet still only be able to reach a particular method via reflection? E.g. if you've got an uncalled private method, it can obviously only be called via reflection - until you add more code to the class that does call the method. This would create an "un-callable under any circumstances (barring reflection) method"Cordey
@Cordey Oh I absolutely agree it shouldn't be allowed; you wouldn't want to create code that needed to be called through reflection. I was just mentioning it for completeness' sake.Misfortune
M
10

The problem is that when an interface member is declared explicitly, the compiler generates a private implementation with an "unpronounceable" name, and provides no means by which code--even within the implementing class--to refer to that implementation.

Basically, when one says void IFoo.Moo(), one is saying that one does not wish to define a name Moo within the class scope; consequently, the compiler won't. In order for private set to work, the member would have to be a "pronounceable" name, and the fact that the member was explicitly implemented is taken as an indication that one does not want the name to be Moo.

In practice, the remedy here is probably the same as for many other cases where it's necessary to have an interface implementation whose name is pronounceable, but which is not exposed publicly under its name: declare an interface implementation which does nothing but chain to other members which have the proper accessibility, e.g. if derived classes should not be able to affect the value the value:

private readonly int _foo = whatever;
public int IFOO.Foo { get {return _foo;}}

or, if derived classes should be able to affect it, either

protected int _foo = whatever;
public int IFOO.Foo { get {return _foo;}}

or

private int _foo = whatever;
protected virtual int setFoo(int value) { _foo = value; }
protected virtual int getFoo() { return _foo; }
public int IFOO.Foo { get {return getFoo();}}

In vb.net, interfaces may be implemented using protected class members, but C# offers no such facility.

Marchpane answered 12/5, 2014 at 16:10 Comment(8)
Nice addition to the existing answers. A small (slightly OT) remark: I remember that in previous .NET versions it was not allowed to prefix a method with get_ or set_, as in your set_Foo. Now it is allowed as long as you do not also have a property declaration by the same name. However, it does not magically turn it into an accessible property though (not sure about reflection).Dunnite
@Abel: I find it irksome when compilers define internal-use member names that can conflict with sensible names of user-defined members, but don't allow access to those members via those names. Personally, I think things like auto-properties and auto-events should have distinct defined names for the properties/events and the associated backing fields, but it's probably too late for that now.Marchpane
I can only agree to that! With generics, the internal names after compilation are uninvocable directly, they use otherwise illegal (as in illegal in C#) characters, like < and > in the names. Why they didn't do so with the property-implementations I don't know. Perhaps in .NET 2.0 they learned from their decisions in .NET 1.0, but didn't backport for compatibility reasons.Dunnite
@Abel: Support for properties is an optional feature in the CLS; consequently any property whose getter and setter are not invokable as methods may not be usable from some .NET languages. I don't think there's a particular .NET requirement that the backing methods start with get_ and set_, but it's a convention. Personally, I'm not sure what advantage there is to .NET even having properties versus saying that if code uses an identifier in a way that looks like a method invocation, and a method exists with get_ or set_ prefixed to that name and it has a certain attribute,...Marchpane
...then regard the name as a 'gettable' or 'settable' property. Perhaps there are situations in Reflection where property definitions are useful, but they seem to add an extra layer of abstraction that doesn't seem all that helpful or expressive. Note, btw, that if there were a runtime type Property<T> which encapsulated a getter and setter, and a property Value that would chain to them, and if one could construct such an object from an instance with an exposed property, that would be useful.Marchpane
@Abel: PS--I find it ironic that for all the claims that C#, rather than VB.NET, is the language for "serious" programmers, VB.NET allows code to explicitly implement an interface number while making the "local" method name usable, which is the proper thing to do when an inheritable class explicitly implements an interface member that includes a non-trivial amount of code.Marchpane
Semi-off-topic: shouldn't _foo be non-readonly in the third example (and perhaps conceptually the second, though the second is still technically legal)?Lanyard
@hydroiodic: I was also missing some public specifiers. Thanks.Marchpane
W
7

I think the core of the problem is that the interface only has what it needs. If it isn't public, it naturally isn't part of the interface. When you explicitly implement the interface it does not, therefore, exist.

In the implicit case, your code fits the interface, but is not totally constrained by it. You can add on more things if needed.

Getting information on why this is would require a designer of the language to answer you. It seems logical to me, however: if it isn't part of the interface, you cannot implement/access it as part of the interface.

Wiper answered 12/5, 2014 at 14:42 Comment(2)
" If it isn't public, it naturally isn't part of the interface." >> no, it is legal to implement an interface with private accessors, and the mandatory absence of an access modifier suggests to me the nature of an explicit interface: it is private (but publicly accessible through a cast to the interface).Dunnite
Added clarification, because you misunderstood my point, and others might too. I agree with Damien's answer 100%. Same thing, different words.Wiper
M
4

The property declaration is an atomic thing containing a getter and a setter. It should match the interface.

If you would allow this, then apparently the getter and setter are seen as different things. In that case there is no use to limiting it to privates either. In that case the interface just dictates that there has to be a property which can be read, and you are free to make it writable as well.

Anyway, apparently it's a design decision to make it atomic.

Mathers answered 12/5, 2014 at 14:44 Comment(1)
That would be a nice feature, being able to add setters or getters to a property that doesn't have both.Blancheblanchette
R
4

Perhaps the explicit interface implementation should not be seen as part of the class itself but rather as a kind of adapter from your interface to the class. Given this view; consider the following implementation:

public interface I {
    string S { get; set; }
}

class C : I {
    public C() {
        this.S = "Hello World"; 
        //compile error: explicit implementation not accessible
    }

    string I.S { get; set; }
}

In class C, the property S is not even privately accessible because it is an explicit implementation. Would it not be bad design in the first place to have a concrete implementation of a field not be accessible by the implementation itself?

Furthermore, in your example creating a setter for the explicit implenentation would never be accessible; since the property is only accessible when cast to the IConnection interface. There it only has a getter.

Reyna answered 12/5, 2014 at 14:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.