How do I get a superclass to reference a property in a subclass when the subclass invokes the superclass' method? - vb.net

The superclass:
Public MustInherit Class Product
Friend _shortName as String = Nothing
Public ReadOnly Property Name as String
Get
return _shortName
End Get
End Property
End Class
The Sub class
Public Class MyProduct : Inherits Product
Friend Shadows _shortName as String = "MyProd"
End Class
So, in the immediate console when I'm debugging, I do:
Dim product as new MyProduct
product.Name ' => Nothing
product.Name should be "MyProd" - but it isn't. How do I set this up correctly, so that the the property defined in the superclass accesses the field defined in the subclass?

There is no way for the base class to access the shadowed version of the field. Shadows should be avoided unless it is absolutely necessary. For something like this, you should just change the value of the base field from the derived class. There is no need to shadow it:
Public Class MyProduct : Inherits Product
Public Sub New()
_shortName = "MyProd"
End Sub
End Class
It's worth mentioning that, unless you really need it to be scoped as Friend, the _shortName field in the base class should be scoped as Protected.
Although, in this example, it looks like you probably want all derived classes to provide the name. In that case, there are two ways to accomplish that. You could require the name as a parameter in the base class' constructor:
Public MustInherit Class Product
Public Sub New(shortName As String)
_shortName = shortName
End Sub
Friend _shortName As String = Nothing
Public ReadOnly Property Name As String
Get
Return _shortName
End Get
End Property
End Class
Public Class MyProduct : Inherits Product
Public Sub New()
MyBase.New("MyProd")
End Sub
End Class
In this case, the _shortName doesn't even need to be Friend or Protected. It should ideally be scoped as Private.
Or, you could simply declare the property as MustOverride:
Public MustInherit Class Product
Public MustOverride ReadOnly Property Name As String
End Class
Public Class MyProduct : Inherits Product
Public Overrides ReadOnly Property Name As String
Get
Return "MyProd"
End Get
End Property
End Class

Related

Retrieve object name

I need to retrieve the name of an instanced object (not the type name...)
I have seen that the GetProperties() function gets the child properties name but i need the name of the current object
Public Class Class1
Private mValore As String
Public Property Valore As String
Get
Return mValore
End Get
Set(value As String)
mValore = value
End Set
End Property
End Class
Public Class Class2
Private mMickey As new Class1
Public Property Mickey As Class1
Get
Return mMickey
End Get
Set(value As Class1)
mMickey = value
End Set
End Property
End Class
I need to obtain inside Class1 the name of instanced object in Class2: "Mickey"
Is it possible ?
Thanks in advice for all that will answer me.
As mentioned by Hans Passant, objects don't have names.
So if you really need names, you may introduce them, as a property or field. You may employ CallerMemberNameAttribute to automatically pass the caller name to e.g. constructor.
Another thing, objects might be created outside Class2, indeed in the Mickey ... Set setter you are assigning mMickey field to an object from somewhere outside, so the object might have a different name. I would prefer to create a copy of object instead of just assignment, then we can assign any name to it and it will not collide with the previous name. An example could be:
Imports System.Runtime.CompilerServices
Public Class Class1
Private mValore As String
Public ReadOnly Name As String
Public Sub New(mValore As String, <CallerMemberName> Optional callerMemberName As String = Nothing)
Me.mValore = mValore
Me.Name = callerMemberName
End Sub
Public ReadOnly Property Valore As String
Get
Return mValore
End Get
End Property
End Class
Public Class Class2
Private mMickey As Class1
Public Property Mickey As Class1
Get
Return mMickey
End Get
Set(value As Class1)
mMickey = New Class1(mValore:=value.Valore)
End Set
End Property
End Class
If Class2 only has one property, you can just get the only property's name
Public Class Class1
Public ReadOnly Property Valore As String
Get
Return GetType(Class2).GetProperties().Single().Name
End Get
End Property
End Class
Public Class Class2
Public Property Mickey As Class1
End Class
Or if it has multiple properties, you can just get the first property's name
Public Class Class1
Public ReadOnly Property Valore As String
Get
Return GetType(Class2).GetProperties().First().Name
End Get
End Property
End Class
Public Class Class2
Public Property Mickey As Class1
Public Property Mouse As String
End Class
That returns the first property in order in which the properties are defined. So if the order is changed, it breaks.
Surely there must be more qualifying information to lead us to a solution. Can I make the assumption that you are only interested in the name of the property whose type is Class1? Then you can also filter on the property's type
Public Class Class1
Public ReadOnly Property Valore As String
Get
Return GetType(Class2).GetProperties().Where(Function(pi) pi.PropertyType Is GetType(Class1)).Single().Name
End Get
End Property
End Class
Public Class Class2
Public Property Mouse As String
Public Property Mickey As Class1
End Class
I think this is exactly what you're looking for. But if not, let me know and we can work it out.

Casting Generic Interface to base type

I'm struggling to get this hierarchy working with generics. The problem is that Items is generic, specified at the inheritance level, therefore I cannot cast back to BaseItem, i.e. casting from SpecialItem(Of ExtraSpecialItem) to IItemHost(Of BaseItem) when SpecialItem inherits BaseGroup, as BaseGroup implements IItemHost.
What am I doing wrong here?
Public MustInherit Class BaseItem
Public Property ItemNameOrSomething As String
End Class
Public Interface IItemHost(Of TItemType As {BaseItem})
Property Items As BindingList(Of TItemType) '-- No Out parameter allowed :(
End Interface
Public Class BaseGroup(Of TGroup AS {BaseItem})
Inherits BaseItem
Implements IItemHost(Of TGroup)
'-- This is the key property, all BaseGroup implimentors need an Items property of their specific type
Public Property Items As New BindingList(Of TGroup)() Implements IItemHost(Of TGroup).Items
End Class
Public Class SpecialItem
Inherits BaseGroup(Of ExtraSpecialItem)
End Class
Public Class ExtraSpecialItem
Inherits BaseGroup(Of LeafItem)
End Class
Public Class LeafItem
Inherits BaseItem
End Class
For the most part, this all actually works. What I cannot do is:
Dim root = New SpecialItem()
root.ItemNameOrSomething = "Testing 1"
root.Items.Add(New ExtraSpecialItem() With {.ItemNameOrSomething = "Testing 2"})
'-- This specifically, no casting options available.
Dim item = CType(root, IItemHost(Of BaseItem))
Dim subItems = item.Items
Dim testing2Text = subItems.First().ItemNameOrSomething '-- = "Testing 2"
Ok this hasn't exactly solved the problem, but it's a solution I'm willing to settle with for now.
If I change my BaseItem to have a "BaseItems" collection, my inherited BaseGroup classes can have a default Items IEnumerable. If I need to write back to this collection, I can simply use BaseItems. For standard looping through the items, I can use Items and this will give me the proper casting.
Public MustInherit Class BaseItem
Public Property ItemNameOrSomething As String
Public Property BaseItems As New BindingList(Of BaseItem)()
End Class
Public Class BaseGroup(Of TGroup As {BaseItem})
Inherits BaseItem
Public ReadOnly Property Items As IEnumerable(Of TGroup)
Get
Return BaseItems.Cast(Of TGroup)()
End Get
End Property
End Class

how to access class from inherited class

I have two classes:
class class2
inherits class1
public sub modify()
'modify property of class1
end sub
end class
How can I modify class1 in a sub in class2?
You just call it. Example:
Public Class class1
Private _Value As String = String.Empty
Property Value() As String
Get
Return _Value
End Get
Set(ByVal value As String)
_Value = value
End Set
End Property
End Class
Public Class class2
Inherits class1
Public Sub modify()
Value = "modified"
End Sub
End Class
And to show it works:
Dim c2 As New class2
c2.modify()
MessageBox.Show(c2.Value)
You are asking about properties, note that only protected and public properties are visible to inherited classes.
You need the MyBase keyword when you are overriding an existing function in the parent class. Other protected or public properties or functions can be accessed regulary without any special keyword.
One tip I wanted to add to the above comments regarding accessing base class info is where you have a base class without a default contructor or want to use a specific constructor This is a good opportunity to use Mybase. You have to call the constructor before any additional actions take place in this scenario.
Public Class MyClass
Inherits baseClass
Public Sub New()
mybase.new("Oranges")
End Sub
End Class
Public Class baseClass
Private _someVariable as String
Public Sub New(byval passedString as string)
_someVariable = passedString
End Sub
End Class

How can I control the element names of serialized subclasses?

Let's say I have the following class structure (simplified from my real-world problem):
Public Class PC_People_Container
Private _people_list As New List(Of PL_Person)
Public Sub New()
End Sub
Public Sub Add(ByVal item As PL_Person)
_people_list.Add(item)
End Sub
Public Property PeopleList As List(Of PL_Person)
Get
Return _people_list
End Get
Set(ByVal value As List(Of PL_Person))
_people_list = value
End Set
End Property
End Class
Public Class PL_Person
Private _Name As String
Public Property Name As String
Get
Return _Name
End Get
Set(ByVal value As String)
_Name = value
End Set
End Property
Private _Contacts As ContactObject
Public Property Contacts As ContactObject
Get
Return _Contacts
End Get
Set(ByVal value As ContactObject)
_Contacts = value
End Set
End Property
Public Sub New()
End Sub
End Class
Public Class ContactObject
Public Property PhoneNumber As String
Public Property EmailAddress As String
Public Sub New()
End Sub
End Class
If I were to serialize this, I'd get the default assigned node names in my XML. That means my root is named PC_People_Container and each person in the list is marked up as PL_Person. I know I can change the root node using <XmlRoot(ElementName:="PeopleContainer")>. The trouble is doing that for the subclasses. I can't use the <XmlRoot> tag on PL_Person class because there can't be two root elements, and IntelliSense throws a fit when I try to use the <XmlElement> tag on a class like I would on a property. Is it even possible to control what those subclasses are named when they're serialized as child nodes?
PL_Person and ContactObject are not subclasses as you call them, they are merely property types.
This makes your question confusing because it suggests you may have a problem with inheritance (subclasses are classes that inherit from some base class) when in fact you just want your property elements to be named differently.
You should decorate your properties (not classes) with <XmlElement> to specify custom name:
<XmlElement("Persons", GetType(PL_Person))>
Public Property PeopleList As List(Of PL_Person)
As an afterthought, I would definitely not recommend calling your classes using such an awkward convention. In .NET, you should not use any prefixes or underscores in class names. Just call it Person.

Can I inherit from a generic class without specifying a type?

I have the following sample code in a VB.NET console application. It compiles and works, but feels like a hack. Is there a way to define EmptyChild so that it inherits from Intermediate(Of T As Class) without using the dummy EmptyClass?
Module Module1
Sub Main()
Dim Child1 = New RealChild()
Child1.Content = New RealClass()
Dim Child2 = New EmptyChild()
Console.WriteLine("RealChild says: " & Child1.Test)
Console.WriteLine("EmptyChild says: " & Child2.Test)
Console.ReadLine()
End Sub
Public Class EmptyClass
End Class
Public Class RealClass
Public Overrides Function ToString() As String
Return "This is the RealClass"
End Function
End Class
Public MustInherit Class Base(Of T As Class)
Private _content As T = Nothing
Public Property Content() As T
Get
Return _content
End Get
Set(ByVal value As T)
_content = value
End Set
End Property
Public Overridable Function Test() As String
If Me._content IsNot Nothing Then
Return Me._content.ToString
Else
Return "Content not initialized."
End If
End Function
End Class
Public MustInherit Class Intermediate(Of T As Class)
Inherits Base(Of T)
'some methods/properties here needed by Child classes
End Class
Public Class RealChild
Inherits Intermediate(Of RealClass)
'This class needs all functionality from Intermediate.
End Class
Public Class EmptyChild
Inherits Intermediate(Of EmptyClass)
'This class needs some functionality from Intermediate,
' but not the Content as T property.
Public Overrides Function Test() As String
Return "We don't care about Content property or Type T here."
End Function
End Class
End Module
The other way to do this would be to move the generic code out of the Base class and then create 2 Intermediate classes like this:
Public MustInherit Class Intermediate
Inherits Base
'some methods/properties here needed by Child classes
End Class
Public MustInherit Class Intermediate(Of T As Class)
Inherits Intermediate
'implement generic Content property here
End Class
Then RealChild would inherit from the generic Intermediate and EmptyChild would inherit from the non-generic Intermediate. My problem with that solution is that the Base class is in a separate assembly and I need to keep the code that handles the generic type in that assembly. And there is functionality in the Intermediate class that does not belong in the assembly with the Base class.
Yes, you need to specify a type parameter when you inherit, or your EmptyChild must be generic as well. But, you don't have to dummy up a EmptyClass - just use Object as your type parameter:
Public Class EmptyClass
Inherits Intermediate(Of Object)
End Class