views:

110

answers:

2

If I set a Friend-level scope on a setter, like this...

Public Class MyClass
    Public Property IsDirty() As Boolean
        Get
            Return _isDirty
        End Get
        Friend Set(ByVal trueFalse As Boolean)
            _isDirty = trueFalse
        End Set
    End  Property
End Class

...And then call it from another project, it works correctly. I can't do something like MyClass.IsDirty = True.

Great! That's exactly what I want.

But now if I define an interface, and I will indeed have to do that:

Public Interface IMyClass
    Property IsDirty() As Boolean
End Interface

I can do something like:

Dim MyInstance as IMyClass= GetSomeInstanceOfMyClass()
MyInstance.IsDirty=True

...And, bizarrely, it runs! No exceptions are thrown, and the inner variable is set to True. It ignores the Friend scope completely!

That's hideous. What am I missing??

Note: I need this because I'm designing an API, and I want the inner API to be able to set IsDirty, but end-developers shouldn't be able to get into that. Currently I am wrapping the whole class in a facade to get this functionality, but the facade should be unecessary.

+1  A: 

What you are missing is the concept of inplicit and explicit interface implementation. See the answer to this question for more details.

And if you think it's hideous with a Friend setter, try setting it to Private and watch it still be accessible via the interface!

chyne
I did. :) When the Friend didn't work, I set it Private. I thought my head was going to explode. Thanks for the help.
Brian MacKay
+1  A: 

Interface methods always have public accessibility. You can't fix that by explicit interface implementation, that will only hide the class method. Simply casting the object to the interface type gives unfettered access again.

EDIT: actually, the problem is easy to solve. Just declare the property ReadOnly in the interface declaration :)

For example:

Public Interface IMyClass
    ReadOnly Property IsDirty() As Boolean
End Interface

Public Class Test
    Implements IMyClass
    Private mIsDirty As Boolean
    Private ReadOnly Property IsDirtyImpl() As Boolean Implements IMyClass.IsDirty
        Get
            Return mIsDirty
        End Get
    End Property
    Public Property IsDirty() As Boolean
        Get
            Return mIsDirty
        End Get
        Friend Set(ByVal value As Boolean)
            mIsDirty = value
        End Set
    End Property
End Class
Hans Passant
Curses! So what do you do here, wrap the class in a facade-style structure and present that to the end developer, while keeping the "normal" class around for internal use?
Brian MacKay
Got an idea, edited my post
Hans Passant
I would probably make the Property ReadOnly as nobugz suggests, and then add a Friend Sub SetIsDirty(ByVal isDirty As Boolean) for internal API use.
chyne
BTW, declaring the property ReadOnly in the interface doesn't work, at least in VB. It doesn't see the read/write property on the class as a match. :/ This is one of the uglier parts of the framework I have encountered.
Brian MacKay
Updated my post again.
Hans Passant
Now that's clever.
Brian MacKay