If I have a class called A and a class called B, if B inherits A that means A is the super class and B is the subclass. I have been asked to describe why class A is not an abstract class but as i see it class A is an abstract class A, as it has been created for Class B to use in the future, is it something to do with Class B not being able to access the fields in Class A as although they are private by default?
Class A looks something like this
Public Class A
StartDate As Date
Men As Integer
Place As String
Public Sub New()
StartDate = Today
Men = 0
Place = ""
End Sub
End Class
Class B Looks like this
Public Class B inherits Class A
Grade As ExamGrade
Public Sub New()
MyBase.New
StartDate = Today
Men = 0
Place = ""
Grade = 'Easy'
End Sub
Public Function setGrade(grade As String)
ExamGrade = grade
End Function
End Class
In order to be abstract, class A must have the MustInherit keyword.
Abstract (MustInherit) means that this class serves as base class only and cannot be instantiated with New. It also allows you to declare abstract (MustInherit) members with no implementation, i.e. no method body. The inheriting classes then must override the abstract members and provide an implementation unless they are abstract themselves (where a third level of deriving classes would then provide an implementation).
Note that you are allowed to call an abstract member. At runtime the implementation of the actual implementing class will be called.
See: MustInherit (Visual Basic)
Members are private if not specified otherwise. Specify them to be Protected to allow descendant classes to see them or Public to allow "everybody" to see them.
See: Access Levels in Visual Basic
Public MustInherit ClassA
Protected StartDate As Date
Protected Men As Integer
Protected Place As String
Public Sub New()
StartDate = Today
Men = 0
Place = ""
End Sub
Public MustOverride Sub Print()
End Class
Public ClassB
Inherits ClassA
Public Grade As String
Public Sub New()
MyBase.New() 'This initializes StartDate, Men and Place
Grade = "Easy"
End Sub
Public Sub SetGrade(ByVal grade As String)
Me.Grade = grade
End Sub
Public Overrides Sub Print()
Console.WriteLine($"Grade = {Grade}")
End Sub
End Class
Now, you can use it like this
Sub Test(ByVal a As ClassA)
a.Print()
End Sub
You can call Test by passing it a ClassB object.
A is not abstract. Abstract means you cannot instantiate the class. It means you MUST inherit it.
Use the abstract keyword to make the class abstract. You can also make methods abstract as well.
If you want B to see certain methods in A, but not to anyone else, use protected keyword.
Sorry, VB uses the MustInherit and MustOverride keywords.
Related
I have a case as follows (VB.NET):
class A (contains some fields)
class B inherits List(Of A)
class C (contains some fields)
class D inherits List(Of C)
There are some common Methods and Functions that I want them to be centralized and act on both classes B & D, without the need to re-write them and keep them updated under both classes.
Those common Methods and Functions act on identical fields (name and type) in both classes A and C.
What should I do? And why to inherit from List(Of T) is considered a bad idea?
My solution was to create new Abstract (MustInherit) class X and moves the common fields from Classes A & C to class X. Then created a 'Friend' Module that has those common methods and functions (targeting the common fields between classes A & C) which takes IEnumerable(Of class X) .. and voila!
Any comments on such implementation??
Thanks
The below is my solution using IEnumerable, but using a shared function in a separate module.
Public MustInherit Class ProbSts 'class X
Public Status As Boolean
Public Probability, Min, Max As Integer
End Class
Public NotInheritable Class SIPRouteProbabilityStatus 'class A
Inherits ProbSts
Public Route As SIPRoute
End Class
Public NotInheritable Class SIPRGProbabilityStatus ' class C
Inherits ProbSts
Public RG As SIPRouteGroup
End Class
Public NotInheritable Class SIPRouteProbabilityStatusCollection ' class B
Inherits List(Of SIPRouteProbabilityStatus)
Public ProbabilityTotal As Integer
Public Function TotalProbability(Optional Status As Boolean? = Nothing) As Integer
Return TotalProbabilityAction(Me, Status)
End Function
End Class
Public NotInheritable Class SIPRGProbabilityStatusCollection 'class D
Inherits List(Of SIPRGProbabilityStatus)
Public ProbabilityTotal As Integer
Public Function TotalProbability(Optional Status As Boolean? = Nothing) As Integer
Return TotalProbabilityAction(Me, Status)
End Function
End Class
Friend Module ProbabilityAction
Public Function TotalProbabilityAction(ProbStsList As IEnumerable(Of ProbSts), Optional Status As Boolean? = Nothing) As Integer
Dim sum As Integer = 0
For Each x In ProbStsList
If Status.HasValue Then
If x.Status = Status.Value Then sum += x.Probability
Else
sum += x.Probability
End If
Next
Return sum
End Function
End Module
There is nothing wrong with inheriting from List(Of T) when done properly. It's hard to give the best solution without seeing your classes and how you're using them, but your solution of inheriting from an abstract class is one way of solving it. However from your description, implementing an interface seems to be a better fit. Convert X to an interface with the common fields and you'll be good to go. The actual fields will stay in class A and C, yet the external functions will know how to access and use them.
What's the difference between using an interface vs. an abstract class in your case?
Interface: it only defines the common fields and methods of the classes and leaves the implementation to them.
Abstract Class: It actually hosts the common fields and methods with the implementation so the classes don't need to duplicate the code in case it is the same.
EDIT I don't know if this is the best solution, because I need to see how all these classes are used in your code, but you can get rid of the module by converting it to a class that inherits from List(Of T) and then inherit this class instead of List(Of T) in your B and D classes. Something like this:
Public MustInherit Class ProbSts 'class X
Public Status As Boolean
Public Probability, Min, Max As Integer
End Class
Public NotInheritable Class SIPRouteProbabilityStatus 'class A
Inherits ProbSts
Public Route As SIPRoute
End Class
Public NotInheritable Class SIPRGProbabilityStatus ' class C
Inherits ProbSts
Public RG As SIPRouteGroup
End Class
Public MustInherit Class ProbabilityAction(Of T)
Inherits List(Of T)
Public Function TotalProbabilityAction(ProbStsList As IEnumerable(Of ProbSts), Optional Status As Boolean? = Nothing) As Integer
Dim sum As Integer = 0
For Each x In ProbStsList
If Status.HasValue Then
If x.Status = Status.Value Then sum += x.Probability
Else
sum += x.Probability
End If
Next
Return sum
End Function
End Class
Public NotInheritable Class SIPRouteProbabilityStatusCollection ' class B
Inherits ProbabilityAction(Of SIPRouteProbabilityStatus)
Public ProbabilityTotal As Integer
Public Function TotalProbability(Optional Status As Boolean? = Nothing) As Integer
Return TotalProbabilityAction(Me, Status)
End Function
End Class
Public NotInheritable Class SIPRGProbabilityStatusCollection 'class D
Inherits ProbabilityAction(Of SIPRGProbabilityStatus)
Public ProbabilityTotal As Integer
Public Function TotalProbability(Optional Status As Boolean? = Nothing) As Integer
Return TotalProbabilityAction(Me, Status)
End Function
End Class
I kept the original name of the module ProbabilityAction, but it's recommended to change it to a more suitable name for a base class. I also followed your way and qualified it with MustInherit, but you can decide if you want that or not.
is it possible to extend different classes with the same generic class?
I tried something like this:
Public Class A
Public Sub TestA()
Debug.Print("Test A")
End Sub
End Class
Public Class B(Of T)
Public Sub TestB()
Debug.Print("Test B")
End Sub
End Class
Public Class C
Inherits B(Of A)
Public Sub TestC()
TestA() '**<-- Thows error 'is not declared'**
TestB()
Debug.Print("Test C")
End Sub
End Class
I basicly have some usercontrols, which derive from Combobox or Textbox and i'd like both to implement some functions(and interfaces) that are defined in a base class. In C++ i'd do it with multi inheritance.
is it possible to extend different classes with the same generic class?
Generics isn't some kind of "workaround" for a lack of multiple inheritance, no. Your class C doesn't derive from A - it just means that the T in B(Of T) would be A in the context of C.
Which instance of A would you expect TestA() to be called on? Creating an instance of C certainly doesn't create an instance of A...
The fact that B(Of T) doesn't use T anywhere should be a warning signal - types which are generic but never use their generic type parameters are generally problematic.
It's hard to know exactly how to help you solve your real problem without more details, but you can't add a common base class in like this, when you also need to derive from other types which aren't under your control.
Perhaps extension methods would help?
You could make both your Combobox and your Textbox classes implement the same interface.
Then you could define extension methods on that interface class.
Thanks to your hint i got this working with extentions
Public Class Form1
Public Interface IA
Property val As String
End Interface
Public Class A
Public Sub test()
Debug.Print("test")
End Sub
End Class
Public Class C
Inherits A
Implements IA
Public Property val As String Implements IA.val
Public Sub TestC()
val = "testxxx"
TestA()
test()
End Sub
End Class
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim ct As New C
ct.TestC()
End Sub
End Class
Module TestModule
<Extension()>
Public Sub TestA(ByVal pvIA As IA)
Debug.Print(pvIA.val)
End Sub
End Module
This way every class can implement it's own 'parent' (like A here) and i don't need to implement the function TestA for every class.
thank you
Say I have a function that does the following in Vb.net
For i as decimal = 0 to mstrItems.count - 1
mstrItems.item(i).activate
next
And I have classes as such that both classes, though different object type, have the function activate.
I would like to iterate my Items as above, having the list include any type of object, not just one specific type, and have the different objects all perform the activate function in the same way, though they are different object types.
Cheers!
Martin
Alternatively, you could define an interface and implement it in all your different Types:
Interface IActivateable
Sub Activate
End Interface
Class MyType1: Implements IActivateable
Sub Activate Implements IActivateable.Activate
'some implementation details here ...
End Sub
End Class
Class MyType2: Implements IActivateable
Sub Activate Implements IActivateable.Activate
'some implementation details here ...
End Sub
End Class
Then you can cast each type as the interface you want to handle:
Dim myList as new list(of IActivateable)
myList.add(new MyType1)
myList.add(new MyType2)
for each obj as IActivateable in myList
obj.Activate
end for
To give you some searchable terminology, what you're looking to do is use polymorphism to make use of the Strategy Pattern.
At its simplest, what you want is to have a base class (possibly abstract, I don't know the VB terminology for these things though) from which your multiple classes inherit. It's a kind of high-level abstraction which represents the common functionality between those classes. Something like this:
Class Car
Overridable Sub Drive()
Throw New NotImplementedException
End Sub()
End Class
Class GasPoweredCar
Inherits Car
Overrides Sub Drive()
' logic for driving a gas-powered car
End Sub
End Class
Class SolarPoweredCar
Inherits Car
Overrides Sub Drive()
' logic for driving a solar-powered car
End Sub
End Class
And so on. The idea is that there's one common thing you're trying to accomplish (Drive in this case) but multiple different implementations would accomplish it in different ways. But since all of those implementations are still a Car then you can treat them as such (which is where polymorphism comes in).
So instead of having a list of GasPoweredCar and a list of SolarPoweredCar you can have a single combined list of Car which contains both. You don't need to know what kind of car it is in order to simply invoke the Drive function.
For i As decimal = 0 To listOfCars.count - 1
listOfCars.Item(i).Drive
Next
Or more simply:
For Each car As Car In listOfCars
car.Drive
Next
The alternative to Davids excellent answer is using Interfaces.
The GasPoweredCar and SolarPoweredCar classes could implement an interface:
interface ICar { void Drive(); }.
Both classes would have their own internal implementation of the Drive method. Then when iterating over Gas or Solar cars you could cast the class to the interface and call the method.
If David's isn't ideal I am Happy to elaborate, just let me know.
An alternative to polymorphism is to use an Interface:
Module Module1
Sub Main()
Dim lstClass As New List(Of IMyInterface)
lstClass.Add(New FirstClass("A"))
lstClass.Add(New SecondClass("B"))
lstClass.Add(New FirstClass("C"))
lstClass.Add(New SecondClass("D"))
For i As Integer = 0 To lstClass.Count - 1
lstClass(i).Activate()
Next i
End Sub
Interface IMyInterface
Sub Activate()
End Interface
Class FirstClass
Implements IMyInterface
Public Property MyProperty As String
Sub New(s As String)
MyProperty = s
End Sub
Sub Activate() Implements IMyInterface.Activate
MsgBox("First class activate: " & MyProperty)
End Sub
End Class
Class SecondClass
Implements IMyInterface
Public Property MyProperty As String
Sub New(s As String)
MyProperty = s
End Sub
Sub Activate() Implements IMyInterface.Activate
MsgBox("Second class activate: " & MyProperty)
End Sub
End Class
End Module
In general, according to the OOP paradigm, my understanding of encapsulation basically says:
If a member is private, it can only be accessed by the class.
If a member is protected, it can only be accessed by the base class and any derived classes.
If a member is public, it can be accessed by anyone.
If I have a nested class, can I declare a property to be accessible only to that class and the parent class it's nested within? For example:
Public Class ContainerClass
Public Class NestedClass
Protected myInt As Integer ' <- this is what I am wondering about '
Protected myDbl As Double ' <- this is what I am wondering about '
Sub New()
myInt = 1
myDbl = 1.0
End Sub
End Class
Private myNestedObject As New NestedClass
' this function is illegal '
Public Sub GrowNestedObject(ByVal multiplier As Integer)
myNestedObject.myInt *= multiplier
myNestedObject.myDbl *= multiplier
End Sub
End Class
In the example, I cannot directly access myNestedObject.myInt or myNestedObject.myDbl from an instance of ContainerClass if those members are Private or Protected. But suppose I don't want to make them Public, because then they are TOO exposed: they can be altered from anywhere, not just within a ContainerClass object. Declaring them Friend would still be too weak as that would allow them to be altered from anywhere within the application.
Is there any way to accomplish what I am going for here? If not, can anyone think of a more sensible way to achieve something like this?
There is no way of doing this directly with a combination of accessibility modifiers.
The best way I can think of doing this is as follows. It involves an extra level of indirection.
Create a Nested Interface with Private accessibility. This will give only the Parent class and nested children access
Add the fields you want access to to that interface
Make the Nested class implement the interface
Make all of the implementations have private accessibility
Now the parent class and only the parent class will have access to those properties and methods.
For Example:
Class Parent
Private Interface Interface1
ReadOnly Property Field1() As Integer
End Interface
Public Class Nested1
Implements Interface1
Private ReadOnly Property Field1() As Integer Implements Interface1.Field1
Get
Return 42
End Get
End Property
End Class
Sub New()
Dim child As Interface1 = New Nested1
Dim x = child.Field1
End Sub
End Class
Based on JaredPar's answer, you could use a Private ChildClass but a Public Interface that reveals only what it sould show :
Public Class ParentClass
Public Interface IChildClass
ReadOnly Property i() As Integer
Sub SomeSub()
End Interface
Private Class ChildClass
Implements IChildClass
Public myInt As Integer
Public ReadOnly Property i() As Integer Implements IChildClass.i
Get
Return myInt
End Get
End Property
Public Sub SomeSub() Implements IChildClass.SomeSub
End Sub
End Class
Public Shared Function GetNewChild() As IChildClass
Dim myChild = New ChildClass()
myChild.myInt = 3
Return myChild
End Function
End Class
Usage :
Dim c As ParentClass.IChildClass = ParentClass.GetNewChild()
MessageBox.Show(c.i)
c.i = 2 ' Does not compile !
c.SomeSub()
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