Return Inherited Generics as Base Generic - vb.net

I have BaseAbstractClass(of T as WebControl) (VB Generics) which inherits WebControl.
BaseAbstractClass is inherited by ConcreteWrapper1, ConcreteWrapper2, and lastly, to shake things up a bit, ConcreteWrapper4. Each of these would inherit BaseAbstractClass using a different class inherited from WebControl.
What I would like to do is have factory that returns a ConcreteWrapper as a BaseAbstractClass(of WebControl). But whenever I try to return a new instance of a ConcreteWrapper I get a compile time conversion error.
[Edit: Code Added]
BaseAbstractClass
Public MustInherit Class BaseAbstractClass(Of T As WebControl)
Inherits WebControl
Protected _item As T
Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
_item.RenderControl(writer)
End Sub
End Class
The other ConcreteWrappers look like this except with different CustomControl
Public Class ConcreteWrapper1
Inherits BaseAbstractClass(Of CustomControlInheritedFromWebControl1)
Public Sub New(ByVal control As CustomControlInheritedFromWebControl1)
MyBase._item = control
End Sub
End Class
Public Class CustomControlInheritedFromWebControl1
Inherits WebControl
//not the correct comment markers but the coloring works better
//do stuff here... Implm not important.
End Class
My Factory
Public Class WebControlFactory
Public Shared Function GetWebControl() As BaseAbstractClass(Of WebControl)
Return New ConcreteWrapper1(New CustomControlInheritedFromWebControl1())
End Function
End Class
[/Edit]
Could I get an explanation of what's going on and why that won't work (and possibly, a solution)?
Thanks!

ConcreteWrapper1 does not inherit from BaseAbstractClass(of WebControl), instead it inherits from BaseAbstractClass(of T)
BAC(of WebControl) is not interchangable with BAC(of T).
If you must use inheritence, you need two levels of abstraction.
WebControl
BAC inherits WebControl
BAC(of T) inherits BAC
Wrapper1 inherits BAC(of int)
Wrapper2 inherits BAC(of string)
Wrapper3 inherits BAC(of Foo)
Wrapper4 inherits BAC(of Bar)
Then you may return all instances of your Wrappers as BAC.
The reason is phrased well by Zooba:
You cannot cast between generic types with different type parameters. Specialized generic types don't form part of the same inheritance tree and so are unrelated types.

Related

How does one go about converting a class to an abstract generic base class from which it derives?

I am currently working in Visual Studio 2013 with VB on .NET 4.5. I have an abstract base class which holds a collection of another abstract base class, like this:
Generic Base Class:
Public MustInherit Class CustomCollection(Of collectionItemType As CollectionItemBase)
Inherits Collections.ObjectModel.KeyedCollection(Of String, CollectionItemBase)
'...
End Class
Base Class Referened by Generic Base Class:
Public MustInherit Class CollectionItemBase
'...
End Class
I have a group of classes that inherit the generic base class "CustomCollection":
Public Class Collection1
Inherits CustomCollection(Of ClassThatDerivesFromCollectionItemBase1)
'...
End Class
Public Class Collection2
Inherits CustomCollection(Of ClassThatDerivesFromCollectionItemBase2)
'...
End Class
'etc...
This is the code I am using to try and convert an instance of Collection1 to a CustomCollection(Of CollectionItemBase):
'...
someProperty = instanceOfCollection1.Cast(Of CustomCollection(Of CollectionItemBase))
'...
This generates the following error at runtime, which to me seems to be nonsense:
"Unable to cast object of type
'd__b1`1[ApplicationName.CustomCollection`1[ApplicationName.CollectionItemBase]]'
to type
'ApplicationName.CustomCollection`1[ApplicationName.CollectionItemBase]'."
It appears to be claiming that it is unable to cast the object to its own type. Any thoughts?
When you use Cast(Of T) on an IEnumerable(Of U) it will cast every individual element to the destination type, so you should not pass the type of IEnumerable(Of T), but T itself.
In your case write your cast as
someProperty = instanceOfCollection1.Cast(Of CollectionItemBase)
This cast returns an IEnumerable(Of CollectionItemBase) that you can use.
But I don't think that was your question. Let's put that single line of code aside and talk about your question. The fact is generics don't work that way. Generic types Of Base/Derived types are totally different types.
In the simplest example, an instance of A(Of T) can't be cast to A(of U) even if T Inherits From U, because A(Of U) is a different type than `A(Of U)..
To replace class names with your real classes: an instance of
Collection1
or say
CustomCollection(Of ClassThatDerivesFromCollectionItemBase1)
can't be cast to
CustomCollection(Of CollectionItemBase)
despite of the fact that
ClassThatDerivesFromCollectionItemBase1
Inherits CollectionItemBase
because
CustomCollection(Of ClassThatDerivesFromCollectionItemBase1)
is a different type than
CustomCollection(Of CollectionItemBase).
This has nothing to do with covariance because (from MSDN):
In the .NET Framework 4, variant type parameters are restricted to
generic interface and generic delegate types.
So it won't work foe classes but works for interfaces. Let me make some changes to your code:
Public Class CollectionItemBase
End Class
Public Class ClassThatDerivesFromCollectionItemBase1
Inherits CollectionItemBase
End Class
Public Class ClassThatDerivesFromCollectionItemBase2
Inherits CollectionItemBase
End Class
Public Interface ICustomCollection(Of Out collectionItemType As CollectionItemBase)
End Interface
Public Class Collection1
Implements ICustomCollection(Of ClassThatDerivesFromCollectionItemBase1)
End Class
The Out modifier in this line
Public Interface ICustomCollection(Of Out collectionItemType As CollectionItemBase)
makes the type parameter covariant, meaning that if you have
Dim instanceOfCollection1 As New Collection1
these two lines will work:
Dim x As ICustomCollection(Of CollectionItemBase) = instanceOfCollection1
Dim y As ICustomCollection(Of Item1) = instanceOfCollection1
Hope that was helpful

How to get the class type in a inherited shared method

Folks;
Code looks like:
Public Class MasterA
Inherits Underling
End Class
Public Class MasterB
Inherits Underling
End Class
Public Mustinherit Class Underling
Sub DoSomething()
Me.GetType 'Using the instance, I can get the class.
end sub
Shared function() as ???? 'How can I define the return type based on the class that inherited me?
'Me.GetType 'Won't work as this is a shared function with no instance 'Me'
End Function
End class
OK. The question is: is there a way to get at the class type from within a shared function that was inherited by another class?
What I'm building is an XML serializer/desrializer as an inheritable class so that classes that inherit it can be serilized to an XML file, and back again. Rather than writing a serializer/deserializer for each type of class I want to do this with, I'd like to just inherit the functionality.
To do that, though, requires that I be able to ascertain the clas that inherited me in the shared function.
You could get the desired behavior with a generic base class, my VB is a little rusty so you might find stray parens or brackets. This would really be the only way to get a type reference to an inheriting class in a shared base class function.
Public Mustinherit Class Underling(Of T)
Sub DoSomething()
Me.GetType 'Using the instance, I can get the class.
end sub
Shared function() As T
' GetType(T) should get the type at this point
End Function
End class
Public Class MasterA
Inherits Underling(Of MasterA)
End Class
Public Class MasterB
Inherits Underling(Of MasterB)
End Class
As a side note it does seem like a rather weird solution to handle XmlSerialization rather than through your own serializer implementation or XmlSerializer

vb.net method constraints

I have a class which I have marked as MustInherit (called BasePage), with a generic method that is marked as MustOverride:
Protected MustOverride Function SaveData(Of T As {BaseClass})(ByVal item As T) As T
What I want to do is force the users of this method to only supply a type of BaseClass, or anything derived from it. Also, when a class derives from BasePage, it should work on only one derived class from BaseClass:
Protected Overrides Function SaveData(Of T As BaseClass)(ByVal item As T) As T
Dim grad As DerivedClass = CType(item, DerivedClass)
Return grad
End Function
However, when I try to do the cast, it flags up the following error:
Value of type 'T' cannot be converted to 'DerivedClass'.
All the documentation I have read suggests that this should work. However, it's not a big problem if it doesn't work, as I can work around by making a non-generic method that only accepts BaseClass.
Any ideas?
All the documentation I have read suggests that this should work.
On the contrary: it can’t work. The type T derives from BaseClass – but nothing in your code tells the compiler that it is convertible to DerivedClass. For example, it could be of type IndependentlyDerivedClass which is a sibling of DerivedClass.
However, the following cast works:
Dim grad As DerivedClass = DirectCast(DirectCast(item, BaseClass), DerivedClass))
Notice that I’m using DirectCast in place of CType. This is a best-practice when casting in class hierarchies since DirectCast only allows such casts (these, and boxing/unboxing conversions) so you minimize the risk of accidentally calling a conversion operator (which can happen when you’re using CType on non-related types).
You typically do something like this
Public MustInherit Class BasePage(Of T As BaseClass)
Public MustOverride Function Savedata(ByVal Item As T) As T
End Class
Public Class derivedPage
Inherits BasePage(Of DerivedClass)
Public Overrides Function Savedata(ByVal Item As DerivedClass) As DerivedClass
Dim grad As DerivedClass = Item
Return grad
End Function
End Class
Public MustInherit Class BaseClass
End Class
Public Class DerivedClass
Inherits BaseClass
End Class

Protected Constructors and MustInherit/ Abstract class

What is the difference between a class with protected constructors and a class marked as MustInherit? (I'm programming in VB.Net but it probably equally applies to c#).
The reason I ask is because I have an abstract class that I want to convert the constructors to shared/static methods. (To add some constraints).
I can't do this because it's not possible to create an instance in the shared function.
I'm thinking to just remove the MustInherit keyword. Will this make any difference?
Thanks.
ETA:
I think i've answered my question, If I remove the MustInherit keyword, I can no longer include the MustOverrides, which are very useful.
With that in mind, is there any way around my problem?
ETA2:
To clarify, I can't do the below unless I remove the MustInherit keyword?
Public MustInherit MyBaseClass
Private Sub New()
End Sub
Protected Function CreateInstance(ParmList) As MyBaseClass
If ParmList is Ok Then Return New MyBaseClass()
End Function
End Class
You can call the Protected constructor using reflection and instantiate the class but you can't instantiate an abstract class in this way. You can declare MustOverride methods in MustInherit classes but Protected constructor can enforce nothing on derived classes.
You should always declare classes that are conceptually abstract as MustInherit. Protected constructors can be useful when you are providing it along with some Public overloads to provide some more functionality to derived classes.
If the class only has a protected constructor, it is still possible to have an instance of the class which can stand on its own. It would require working around the protected constructor, such as using reflection.
If the class is marked as MustInherit, it is impossible to have an instance of that class on its own. Instances can only be created of the derived/inherited classes.
Not really sure what you want.
If you need to create an object of the abstract class, I recommend you create a private class implementation of your abstract class and return it in your CreateInstanceMethod:
Public MustInherit MyBaseClass
Private BaseClassImplementation
Inherits MyBaseClass
...
End Class
Public Function CreateInstance(paramList) as MyBaseClass
If paramList Is Ok Then Return New BaseClassImplementation
End Function
End Class
However, if you want to add some constraints to the construction, I recommend to throw exceptions:
Public MustInherit MyBaseClass
Protected Sub New(paramList)
If paramList IsNot Ok Then Thow New Exception
...
End Sub
End Class

How do I code these generic functions and classes inheritances correctly? (VB .NET)

I have a function which I need to call for three different types, with the underlying logic remaining the same for all the different types, so I figured it would be best to write this function using generics.
Here is the basic outline of the classes and functions involved:
'PO Base class'
Public MustInherit Class ProductionOrder
Public MustInherit Class Collection(Of T)
Inherits System.Collections.Generic.Dictionary(Of Long, T)
End Class
'....'
End Class
Public Class ProfileProductionOrder
Inherits ProductionOrder
Public Class Collection
Inherits ProductionOrder.Collection(Of ProfileProductionOrder)
End Class
'....'
End Class
Public Class UnitProductionOrder
Inherits ProductionOrder
Public Class Collection
Inherits ProductionOrder.Collection(Of UnitProductionOrder)
End Class
'....'
End Class
Public Class CrateProductionOrder
Inherits ProductionOrder
Public Class Collection
Inherits ProductionOrder.Collection(Of CrateProductionOrder)
End Class
'....'
End Class
'Generic function, intended to work on profile, unit, and crate production orders.'
'This method resides in the base class of the GUI.'
Protected Sub FillPOCells(Of T As ProductionOrder.Collection(Of ProductionOrder)) _
(ByVal dgv As DataGridView, ByVal ProductionOrders As T)
'...do some stuff'
End Sub
'This function resides in the Profile child GUI class.'
Protected Sub LoadDataGridViewPOs()
Dim dgv As DataGridView
Dim ProductionOrders As ProfileProductionOrder.Collection
'....'
'Fill PO Cells'
FillPOCells(Of ProfileProductionOrder.Collection)(dgv, ProductionOrders)
'....'
End Sub
The ProductionOrder base and child classes compile, as does the FillPOCells function. But when I call FillPOCells inside LoadDataGridViewPOs the compiler complains that "Type argument 'ProfileProductionOrder.Collection' does not inherit from or implement the constraint type 'ProductionOrder.Collection(Of ProductionOrder)'.
Also, here is some explanation about why things are set up this way. My predecessor set up the convention of putting the collection of an object as a subclass within it, so it's easy to refer to it as Obj.Collection. Next, the reason we need three different types of production orders is because they are treated differently and stored in different tables and such on the back end. Lastly, I realize I could implement this fairly easily without getting this particular generic function to work, but I'm looking at this as a learning experience to improve my understanding of generics and OO design.
So the question is, why am I getting that compiler error and how should I change my class and generics design to accomplish what I have in mind?
If you need any further explanation about what I'm trying to do or how I have things set up let me know. The main idea is to have a function that can take a collection who's elements belong to one of the ProductionOrder child classes, and run operations on these elements that only use the functionality held in their ProductionOrder base class (hence why either of the child types is okay to operate on in the function).
'PO Base class'
Public MustInherit Class ProductionOrder(Of T)
Public MustInherit Class Collection
Inherits System.Collections.Generic.Dictionary(Of Long, T)
End Class
'....'
End Class
Public Class ProfileProductionOrder
Inherits ProductionOrder(Of ProfileProductionOrder)
'....'
End Class
Public Class UnitProductionOrder
Inherits ProductionOrder(Of UnitProductionOrder)
'....'
End Class
Public Class CrateProductionOrder
Inherits ProductionOrder(Of CrateProductionOrder)
'....'
End Class
That is a lot simpler according to me.
But I higly doubt you need the CollectionClass.