Access a class property only from a certain class - vb.net

Suppose I have the following class:
Public Class lbMenu
Inherits Form
...
Public Sub AddItem(header As String)
Dim item As New lbMenuItem()
item.Owner = Me
item.Header = header
Controls.Add(item)
End Sub
...
End Class
and also the following class:
Public Class lbMenuItem
Inherits Control
Private _Owner As lbMenu
Public Property Owner As lbMenu
Get
Return _Owner
End Get
Set(value As lbMenu)
_Owner = value
End Set
End Property
Private _Header As String
Public Property Header As String
Get
Return _Header
End Get
Set(value As String)
_Header = value
End Set
End Property
...
End Class
As you can see, I access the Owner property of lbMenuItem from the class lbMenu.
What I want is that nobody else can access the Owner property, or maybe just to read the value of the property. I think there is no way to do that but maybe there is a solution to reach a similar situation.
Any suggestions?
Edit:
I already considered passing the owner as a parameter in the constructor, but I want that anyone can access the lbMenuItem class. Anyone can create a new instance of the class, read and write many properties, etc. The only thing I don't want is that someone can write the Owner property. Someone can create a new instance of the class without having to pass an ownerMenu parameter to the New method (he can't know what ownerMenu is for). For the same reason, lbMenuItem can't be a nested class of lbMenu.
I'm trying to get the same result as with ContextMenuStrip and ToolStripMenuItem. ToolStripMenuItem has an Owner property (which in this case has read/write access) and an OwnerItem which is read-only.
So, is there a way to make the Owner property read-only but writable from the lbMenu class?

You could just have the Owner property private or read-only if you want to allow reading its value and set it in the constructor.
Edit:
Someone can create a new instance of the class without having to pass
an ownerMenu parameter to the New method
Well, you can have two constructors, one that accepts a lbMenu parameter, and one without. So, your code would look something like this:
Public Class lbMenu
Public Sub AddItem(header As String)
Dim item As New lbMenuItem(Me)
item.Header = header
Controls.Add(item)
End Sub
End Class
Public Class lbMenuItem
' Use this instead if you don't want the property to be accessible at all.
'Private Property Owner As lbMenu
Public ReadOnly Property Owner As lbMenu
Public Property Header As String
' Allow creating an instance of the class without having to pass/set the owner.
Public Sub New()
End Sub
Public Sub New(ownerMenu As lbMenu)
Owner = ownerMenu
End Sub
End Class
One last recommendation:
Based on the names of your classes, you might consider having a parent/child relationship between them (i.e., the lbMenuItem class would be a nested class within lbMenu). This will give you the advantage of being able to access the private members of lbMenu from a lbMenuItem instance.
Hope that helps.
Addressing the new points in your edit:
ToolStripMenuItem has an Owner property ... and an OwnerItem which is read-only.
That's exactly what I proposed (i.e., to use a read-only property). Note that the OwnerItem property isn't exposed as writable to any other class.
So, is there a way to make the Owner property read-only but writable
from the lbMenu class?
No, that's not possible. A read-only property is read-only, period. You can't expose its setter to a specific class. You can, however, limit the accessibility to its setter by using the keywords Friend, Private, or Protected:
Friend Set --> will make the setter accessible (make the property writable) inside the current project and read-only outside the project.
Private Set --> will make the setter accessible from the child classes.
Protected Set --> will make the setter accessible from the derived classes.
Take a look at this question to get a better idea on how to limit the accessibility of a property setter. It's about C#, but the concept is the same.

Related

Is there anything particular you have to do to make a constructor function?

So to start this off; I'm a beginner in VisualBasic.Net and my classes require me to learn it. The current subject is object constructors and constructor methods. The current exercise (it's not graded or an exam) is requiring us to make a parent class with a constructor method, and a child class with a new() that calls said function. It looks a bit like this;
Protected MustInherit Class Vehicle()
Protected ReadOnly Property Serial_No As Integer
Protected Property Mileage As Integer
Protected Property Color As String
Protected Function CreateVehicle() As Object
End Function
End Class
Public Class Car
Inherits Vehicle
Public ReadOnly Property Car_Type As String
Public Sub New()
End Sub
End Class
The thing I'm having issues with is that I'm not sure how to go about it? Can't ReadOnly properties ONLY be edited in the constructor itself, and doesn't the object need to be initialized in the constructor? Is there something particular I need to add in the CreateVehicle function?
I did ask the teacher but his answer was 'just give up on it and go do something else', which is ultimately pretty unhelpful.
Edit: (added the inheritance to the child class)
So, after being asked for clarification on what I'm trying to do; the exercise itself is not entirely about doing this, but it is the thing in the exercise that I'm struggling with. The goal is to create a Car object utilizing the constructor (New()), but the constructor must call a secondary function located inside the parent class, Vehicle.
My issue is the following : I'm not sure how to go about implementing the function inside the constructor. I know how to call methods/subs/functions and how to get returns from them, but I'm not sure on how I would go about returning a ReadOnly property's values from a secondary function. Don't readonly properties become uneditable outside of the constructor?
I could always return each value separately instead of as an object, and then set the Car object's values to be equal to the return of the function, individually. But then what's the point of calling a separate function instead of just passing everything as a parameter and doing it directly in the constructor?
This is probably what your teacher is looking for:
Public MustInherit Class Vehicle
Protected ReadOnly Property Serial_No As Integer
Protected Sub New(serialNumber As Integer)
Me.Serial_No = serialNumber
End Sub
End Class
Public Class Car
Inherits Vehicle
Public ReadOnly Property Car_Type As String
Public Sub New(serialNumber As Integer, carType As String)
MyBase.New(serialNumber)
Me.Car_Type = carType
End Sub
End Class
Both constructors take in parameters so the ReadOnly properties can be set.

How To Access A Shared Property Of A Class Passed As A Type Parameter

I'm trying to access a shared property of a class passed as a parameter to a type-parametrised procedure. The reason why I'm doing this is so I can embed the various API call endpoints (among other class-specific things) as properties within the class itself. I've read some similar SO posts but nothing is close enough to be sure that it isn’t possible (which I think is likely).
Below is the essence of the structure - there's some pseudo code towards the end:
MustInherit Class BaseClass
Shared Property Endpoint As String
End Class
Class Person
Inherits BaseClass
Property Age As Integer
Property Name As String
Sub New()
_Endpoint = "/GetPerson"
End Sub
End Class
Class Event
Inherits BaseClass
Property When As Date
Property Type As String
Sub New()
_Endpoint = "/GetEvent"
End Sub
End Class
Function Retrieve(T As BaseClass)(Id As String) As T
Dim oResp As HttpResponse = MakeGetCall(T.Endpoint, Id) <- T.Endpoint throws a compile error
Return Deserialize(Of T)(oResp.Content)
End Function
Dim oPerson As Person = Retrieve(Of Person)("123")
Dim oEvent As Event = Retrieve(Of Event)("123")
To my tiny mind, I would have thought that, since T’s base class is BaseClass which contains the property Endpoint, I’d be ok. But seemingly not.
I've tried a fair few things from here on SO and other places to overcome this to no avail. Yes, I realize I could perform some kind of endpoint look-up based on the type of T but the above represents a very clean solution and I’d like to get it to work if possible.
Any ideas?
Assuming you want EndPoint to be different for each subclass, you should use MustOverride instead of Shared...
MustInherit Class BaseClass
Public MustOverride Property EndPoint As String
End Class
Then return a constant in each subclass
Class Person
Inherits BaseClass
Public Overrides Property EndPoint As String
Get
Return "/Person"
End Get
You might want to declare EndPoint as ReadOnly too.
The small limitation is that you'll need an instance of the class to access EndPoint (since it isn't Shared). If you have a parameterless constructor, you could use (New Person).EndPoint where needed.

Shorthand Notation on an Overrideable Property in VB.Net Not Working

In my VB.Net code, I know that when I declare a property in a class I can do so using the shorthand of, for example, Public Property Prop1 As String and .Net automatically creates a private variable _Prop1 that I can refer to within my class.
I also know that I can refer to that variable within my code using either _Prop1 or by Prop1.
Now since I've always assumed that the preferred method is to use the private variable, I've always tried to make modifications / write code within my class referring to _Prop1. This is where my problem now comes in...
I have the following situation:
Public Class MyClass_Base
Public Overridable Property Prop1 As String = "val1"
Public Sub Test()
If _Prop1 = ....
End Sub
End Class
Public Class MyClass
Inherits MyClass_Base
Public Overrides Property Prop1 As String = "val2"
End Class
Basically, I define the property as Overridable in my base class, now when I get to the Test() subroutine, _Prop1 has the value of Nothing. However Prop1 has the correct value of val2.
Programming gurus out there, what is the correct way to deal with this situation?
Do I ignore the auto-created prive variable _Prop1 and work with the public Prop1 or is there something else I should add in / not even use the shorthand notation for this and write my own getter / setter logic to ensure things change as I would like?
Thanks!!!
I think you've answered your own question. You should absolutely not rely on hidden compiler mechanics in your own code. You're referencing a variable that you did not declare and there is no guarantee in the framework that this variable will be there (or work as you might expect, as you've discovered) - it's a hack to use it, so don't.
Unless you have a very good reason not to, code in the class should reference the public property just the same as code using the class would. If you're using automatic properties then there is no difference between doing that and using the private variable. It also has the benefit that if you do, at a later time, decide to implement explicit getters and setters that your code does not break and that your new getters and setters get called.
To explore the reason you get the unexpected result, in declaring
Public Overrides Property Prop1 As String = "val2"
You end up with two different _Prop1 variables - MyClass_Base._Prop1 and MyClass._Prop1. When you then call Test(), you are calling the base class method and that will refer to the base class's _Prop1 variable which has not been set to anything since you have overriden the implicit variable, getter, and setter in the subclass.
To illustrate the point, this is similar to :
Public Class MyClass_Base
Private _Prop1 As String = "val1"
Public Sub Test()
Console.WriteLine(_Prop1)
End Sub
End Class
Public Class MySubClass
Inherits MyClass_Base
Private _Prop1 As String = "val2"
End Class
Sub Main()
Dim class1 As New MyClass_Base
Dim class2 As New MySubClass
class1.Test()
class2.Test()
End Sub
Where your output will be :
val1
val1
In the above case MyClass_Base._Prop1 is always initialized, however, while in your case, it is not. In either case, the Test() method belongs to the base class so it will always refer to its own _Prop1 and not any variables of the same name declared in subclasses.
If you do need to refer to the private field, for whatever reason, you have to be very careful about how you do it (and the implications that follow). Any method that does so would need to itself be Overridable if subclasses are intended to work with their own private variables in the same way. Not to suggest that you should continue to use implicit variables, but to demonstrate :
Public Class MyClass_Base
Public Overridable Property Prop1 As String = "val1"
Public Overridable Sub Test()
Console.WriteLine(_Prop1)
End Sub
End Class
Public Class MySubClass
Inherits MyClass_Base
Public Overrides Property Prop1 As String = "val2"
Public Overrides Sub Test()
Console.WriteLine(_Prop1)
End Sub
End Class
Here we get the "expected" result because MySubClass overrides test to reference its own private field. Better to just stick to using the property names.
From the documentation on Auto Implemented Properties:
"Attributes specified for the property do not apply to the backing field."
This is one of those areas where C# and VB.NET need to be aligned.
C# (correctly) does not allow you to access the auto implemented property backing field (without some convoluted work). I honestly don't know what you can access this in VB.
So the rule here is even though you can access the backing field of an auto implemented property you really shouldn't modify this directly (nor should you need to)
If you need to then you should use a defined backing field (with initialisation) and explicit Get and Set

Make static member persistent

I have one class with a private static (shared, since I'm in VB.BET) field and its associated public static property, since it stores one variable that should be the same to all the instances of this class.
My Class looks like this:
Public MustInherit Class NitrogenController
Private _active As Boolean
Private Shared _controlInterval As TimeSpan
Private _lastControlTime As Date
Public Property Active() As Boolean
Public Shared Property ControlInterval() As System.TimeSpan
'other properies that must be persisted
Public Function Control() As Boolean
If Not Now > _lastControlTime.Add(_controlInterval) Or Not _active Then
Return False
Else
DoControl()
_lastControlTime = Now
Return True
End If
End Function
End Class
The problem arrives when trying to binary serialize these kind of objects, since this shared field is nos being properly stored and returns to its default value when deserializing.
I suppose this is the expected behaviour, so my question is... how can I make a shared field persistent? I have read some comments to similar questions that say that this is a bad design, but it really makes sense (AFAIK) in my case, since this variable should be the same to all the object, but can be changed by the user and therefore should be stored.
Can you suggest another way of doing it?
Thanks!
What you have read, in my opinion, is correct. This is, likely, a bad design. However, if you must, there are two ways to do this with the XmlSerializer. The easy way would be to simply add a public instance (non-shared) property which has a getter and setter which simply wrap the shared property, for instance:
Public MustInherit Class NitrogenController
Public Shared Property ControlInterval As TimeSpan
Public Property CurrentControlInterval() As TimeSpan
Get
Return ControlInterval
End Get
Set(value As TimeSpan)
ControlInterval = value
End Set
End Property
End Class
If you aren't satisfied with that method, the second, more involved, option would be to override the default serialization logic by implementing the ISerializable interface.

After restricting Setter scope and then applying an interface, scope is disregarded!

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.
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
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!