I have an abstract "GridBase" class with two types of derived classes "DetailGrid" and "HeaderGrid".
Respectively, one is comprised of "DetailRow" objects and the other "HeaderRow" objects. Both of those inherit from a "RowBase" abstract class.
What I am trying to do is the following:
Public MustInherit Class GridBase
Private pRows As List(Of RowBase)
Public ReadOnly Property Rows As List(Of RowBase)
Get
Return pRows
End Get
End Property
End Class
Public Class DetailGrid
Inherits GridBase
End Class
In this scenario, I want DetailGrid.Rows to return a list of DetailRow. I want HeaderRow.Rows to return a list of HeaderRow. Am I on the right track with this or should the Rows property not be included in the GridBase class?
If you want a stronger typing guarantee, then you probably want:
Public MustInherit Class GridBase(Of T as RowBase)
Private pRows As List(Of T)
Public ReadOnly Property Rows As List(Of T)
Get
Return pRows
End Get
End Property
End Class
Public Class DetailGrid
Inherits GridBase(Of DetailRow)
End Class
Related
I'm already searching for hours, but I cannot find any solution or even a possible way, that fits.
I need a way to enforce, that multiple classes have a shared member. This classes can not be inherits another class, because they are entity data model classes and i work on a partial class file beside the autogenerated files.
I tried to use an interface, but interfaces does not provide the option to declare a member as shared.
My try was like:
Public Interface IInterfaceA
ReadOnly Property PropA as String
End Interface
Public Partial Class ClassA
Implements IInterfaceA
Public Shared ReadOnly Property SPropA As String
Get
Return "FixedValueStringForClassA"
End Get
End Property
Public ReadOnly Property PropA As String Implements IInterfaceA.PropA
Get
Return SPropA
End Get
End Property
End Class
Public Partial Class ClassB
Implements IInterfaceA
Public Shared ReadOnly Property SPropA As String
Get
Return "FixedValueStringForClassB"
End Get
End Property
Public ReadOnly Property PropA As String Implements IInterfaceA.PropA
Get
Return SPropA
End Get
End Property
End Class
So I can call the PropA Member, when I get e.g. an array of types, that implements IInterfaceA.
But my first implementation does not enforce the shared property, only the normal property. To call the normal property, I would need an instance of that type, but this is not the way I searching for.
Maybe there is another solution to this problem.
Unfortunately you can't force a class to implement a shared member.
However, you CAN share extension methods from a module.
I had a similar problem recently with needing to share functions with multiple classes but still be able to run them as that class. Which I solved using extension methods.
Basically how it works is the module extends all objects that implement an interface, which can be conveniently included with the module, that interface will include anything the module will need access to from the calling class and the module will include anything that needs to be shared between all classes
Public Module SharedProp
Dim PropA As String
<Extension()>
Public Function GetPropA(Of T As IInterfaceA)(this As T) As String
Return PropA
End Function
<Extension()>
Public Sub SetPropA(Of T As IInterfaceA)(this As T, value As String)
PropA = value
End Function
Public Interface IInterfaceA
Property PropA As String
End Interface
End Module
so long as the module is included, any class which impliments your interface will have access to the module's extension methods, which in turn has access to the module's implicitly shared members
Imports SharedProp
Public Partial Class ClassA
Implements IInterfaceA
Public Property PropA As String Implements IInterfaceA.PropA
Get
Return Me.GetPropA()
End Get
Set(value As String)
Me.SetPropA(value)
End Set
End Property
End Class
Public Partial Class ClassB
Implements IInterfaceA
Public Property PropA As String Implements IInterfaceA.PropA
Get
Return Me.GetPropA()
End Get
Set(value As String)
Me.SetPropA(value)
End Set
End Property
End Class
Hope that helps solve your problem!
EDIT: Shared on Class basis instead of Interface
Public Module SharedProp
Dim PropA As Dictionary(Of Type, String)
<Extension()>
Public Function GetPropA(Of T As IInterfaceA)(this As T) As String
If PropA.ContainsKey(GetType(T)) Then
Return PropA(T)
Else
Return Nothing
End Function
<Extension()>
Public Sub SetPropA(Of T As IInterfaceA)(this As T, value As String) As String
If PropA.ContainsKey(GetType(T) Then
PropA(T) = value
Else
PropA.Add(GetType(T), value)
End If
End Function
Public Interface IInterfaceA
Property PropA As String
End Interface
End Module
Consider a class named Fruit and a class named FruitStand which contains a generic List of Fruit and exposes a property (Contents) to obtain the List of Fruit for the pupose of iterating the Fruit in the class.
Now I want to create a class Apple that is a subclass of Fruit and I want a class called AppleCart that is a subclass of FruitStand. The contents of the generic List in AppleCart will only ever contain intances of the Apple class. What I want is to be able to have a consumer of AppleCart not have to cast the elements returned by the Contents property to Apple from Fruit.
I am hoping this is something simple that I am just having a mental block over.
You could use generics.
Sub Main()
Dim cart As New AppleCart
cart.Fruits.Add(New Apple)
End Sub
Class FruitStand(Of T As Fruit)
Public Property Fruits As List(Of T)
End Class
Class Fruit
End Class
Class AppleCart
Inherits FruitStand(Of Apple)
End Class
Class Apple
Inherits Fruit
End Class
You can use a generic constraint on the FruitStand class, which forces a derived class to specify a class which derives from Fruit in its declaration
Sub Main()
Dim ac As New AppleCart({New Apple(), New Apple()})
For Each a In ac.Fruits
a.Rot() ' prints "The apple is rotten!"
Next
Console.Read()
End Sub
Public Class Fruit
Public Overridable Sub Rot()
Console.WriteLine("The fruit is rotten!")
End Sub
End Class
Public Class Apple
Inherits Fruit
Public Overrides Sub Rot()
Console.WriteLine("The apple is rotten!")
End Sub
End Class
Public Class FruitStand(Of T As Fruit)
Private _fruits As List(Of T)
Public ReadOnly Property Fruits As IEnumerable(Of T)
Get
Return _fruits
End Get
End Property
Public Sub New(fruits As IEnumerable(Of T))
_fruits = fruits.ToList()
End Sub
End Class
Public Class AppleCart
Inherits FruitStand(Of Apple)
Public Sub New(fruits As IEnumerable(Of Apple))
MyBase.New(fruits)
End Sub
End Class
I'm seeing 3 options: you either make your FruitStand generic like so:
Class FruitStand(Of TFruit As Fruit)
Public ReadOnly Property Contents As List(Of TFruit)
End Class
NotInheritable Class AppleCart
Inherits FruitStand(Of Apple)
End Class
Or you sever the relationship between FruitStand and AppleCart and instead derive them both from the same, newly extracted base class (I think this is the neatest option):
MustInherit Class FruitStandBase(Of TFruit As Fruit)
Public ReadOnly Property Contents As List(Of TFruit)
End Class
NotInheritable Class FruitStand
Inherits FruitStandBase(Of Fruit)
End Class
NotInheritable Class AppleCart
Inherits FruitStandBase(Of Apple)
End Class
In both cases I've sealed the derived AppleCart as no type that derives from it will be able to override the type of TFruit. At that point in the hierarchy the type of Contents is closed.
Finally, you may be able to use shadowing to change the meaning of Contents in the more derived types. The catch is: you have to use a covariant collection for your base type's Contents property (which means using a read-only interface such as IReadOnlyList(Of Fruit) or an array - either way you're losing the ability to add or remove elements):
Class FruitStand
Public ReadOnly Property Contents As IReadOnlyList(Of Fruit)
Public Sub New()
Me.New(New List(Of Fruit))
End Sub
Protected Sub New(contents As IReadOnlyList(Of Fruit))
Me.Contents = contents
End Sub
End Class
Class AppleCart
Inherits FruitStand
Public Shadows ReadOnly Property Contents As IReadOnlyList(Of Apple)
Get
Return CType(MyBase.Contents, IReadOnlyList(Of Apple))
End Get
End Property
Public Sub New()
MyBase.New(New List(Of Apple))
End Sub
End Class
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
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
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