vb.net method constraints - vb.net

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

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

Issue with generics, interfaces, and casting

I recently added an interface to some custom user controls I have implemented. The interface is pretty basic. It has one method that supports chaining:
Public Interface IMyInterface(Of T As WebControl)
Function DoSomething() As T
End Interface
The implementations are also pretty basic:
Public Class MyCustomControl
Inherits CompositeControl
Implements IMyInterface(Of MyCustomControl)
Public Function DoSomething() As MyCustomControl _
Implements IMyInterface(Of MyCustomControl).DoSomething
' do stuff
Return Me
End Class
Everything works fine up to this point. The issues arise when I attempt to loop over a collection of controls that all implement the IMyInterface interface, like so:
Dim myList = New List(Of IMyInterface(Of WebControl))
myList.Add(someCustomControl)
myList.ForEach(Sub(i) i.DoSomething())
someCustomControl is a MyCustomControl which implements IMyInterface(Of MyCustomControl) instead of IMyInterface(Of WebControl).
I am getting this error on the second line (where I try to add someCustomControl):
Option Strict On disallows implicit conversions from 'MyCustomControl' to 'IMyInterface(Of WebControl)'.
Is there any way to get around this error? I am close to having it working but I do not know enough about generics to get beyond this point.
Covariance is a language feature that was introduced in VS 2010, and solves your problem. You need to define your generic such that the type T has the Out keyword in front of it:
Public Interface IMyInterface(Of Out T As WebControl)
Function DoSomething() As T
End Interface
When you use the Out keyword, you are using covariance. It allows generics of a more derived type to be used in place of a generic with the base type. So in your case it will allow a IMyInterface(Of MyCustomControl)) object in places where the code would normally expect IMyInterface(Of WebControl)), such as your for loop.
Note that covariance has a restriction. The covariant type T can only be used as a function return value, and not as a parameter into a function (or sub). For example, if the DoSomething signature in IMyInterface looked like this the compiler would complain:
' Here the type T is used as an input param - compiler error
Sub DoSomething(ByVal sampleArg As T)
Given your chaining scenario, I don't think the above restriction is a problem.
More Info at MSDN:
Covariance and Contravariance
Creating Variant Generic Interfaces
I don't know what your function DoSomething does, but I try assigning the instance's CssClass in there for testing purpose.
Declare the interface as follows:
Public Interface IMyInterface(Of Out T As WebControl)
Function DoSomething() As T
End Interface
Notice the Out T parameter.
Create 2 controls that implement the interface:
Public Class MyCustomControl1
Inherits CompositeControl
Implements IMyInterface(Of MyCustomControl1)
Public Function DoSomething() As MyCustomControl1 Implements IMyInterface(Of MyCustomControl1).DoSomething
' do stuff
Me.CssClass = "XXX"
Return Me
End Function
End Class
Public Class MyCustomControl2
Inherits CompositeControl
Implements IMyInterface(Of MyCustomControl2)
Public Function DoSomething() As MyCustomControl2 Implements IMyInterface(Of MyCustomControl2).DoSomething
' do stuff
Me.CssClass = "YYY"
Return Me
End Function
End Class
On a test page's PageLoad event:
Dim someCustomControl As New MyCustomControl1
Dim someCustomControl2 As New MyCustomControl2
Dim myList = New List(Of IMyInterface(Of WebControl))
myList.Add(someCustomControl)
myList.Add(someCustomControl2)
myList.ForEach(Sub(i) Literal1.Text &= i.DoSomething.CssClass & "<br />")
The result is, the CssClass property of both someCustomControl & someCustomControl2 are set to the respective values.
This shows that the interface function DoSomething was successfully called and the instance changed.
You will need to cast the object before adding it:
myList.Add(CType(someCustomControl, IMyInterface(Of WebControl)))
You may also want to concider making the interface not generic and your "DoWork" method return type as the interface itself.
Public Interface IMyInterface
Function DoSomething() As IMyInterface
End Interface
When you have to specify the type in the interface definition it kind of takes away from the power of interfaces (not having to know about the implementation).

Error when passing friend class as type from public class to friend base class

Disclaimer: I am fairly new to working with generics so I am not entirely sure if what I am trying to do even makes sense or is possible.
I have a bunch of user controls in a project. All of these user controls share a similar property so I want to move it into a base class. The only difference is the return type of the property.
I have three classes interacting in this scenario. The first class is a base type, which inherits from CompositeControl and will be inherited by other classes in my project:
Friend Class MyBaseClass(Of T As {New})
Inherits CompositeControl
Private _someProperty As T = Nothing
Protected ReadOnly Property SomeProperty As T
Get
// dumbed down for the sake of example
If _someProperty Is Nothing Then
_someProperty = New T()
End If
Return _someProperty
End Get
End Property
End Class
Then I have this control class, which inherits from MyBaseClass:
Public Class MyControlClass
Inherits MyBaseClass(Of MyReturnTypeClass)
// snip...
End Class
And finally MyReturnTypeClass which is what the base's SomeProperty should return:
Friend Class MyReturnTypeClass
Public Property AutoProperty1 As Boolean = False
Public Property AutoProperty2 As String = String.Empty
// etc
End Class
When I attempt to build the project, I get this error from MyControlClass:
Inconsistent accessibility: type argument 'MyReturnTypeClass' is less accessible than Class 'MyControlClass'.
I need MyControlClass to be Public so it can be consumed by other projects, and I also want the MyBaseClass and MyReturnTypeClass to be Friend so they cannot be seen/used by consumers. Am I just missing some special keyword somewhere or is this not possible?
You cannot inherit from a base class that is less accessible than the derived class. So for instance, this won't work:
Friend Class MyBase
End Class
Public Class MyDerived
Inherits MyBase ' Won't compile because MyBase is less accessible
End Class
Therefore, since in your example, MyBaseClass(T) has is a friend type, but you are trying to inherit from it into a public MyControlClass type. Therefore, even if you took generics and MyReturnTypeClass out of the "equation", it still wouldn't work.
However, with generics, even if no member of the public interface of the class actually uses the generic type, the type must still be at least as accessible as the derived type. For instance:
Public Class MyBase(Of T)
' T not actually used at all
End Class
Friend Class MyOtherType
End Class
Public Class MyDerived
Inherits MyBase(MyOtherType) ' Won't compile because MyOtherType is less accessible
End Class
The base class must be at least as accessible as the derived class. This is a language restriction (see here).
If you intend to avoid MyBaseClass being instantiated by consumers, consider marking it Public MustInherit instead of Friend. Hope this helps.

Can I have a function with return type of the class itself in inherited classes? VB.NET

I have a parent class that is also a factory. For example:
Public Class Factory
Public Function clone() as Factory
' Some logic here
' return something
End Function
Public Function all() as List (Of Factory)
' Some logic here
' return something
End Function
End Class
And then an inherited one
Public Class BookFactory
inherits Factory
End Class
I can use inflection in the Factory class to generate the proper extended objects when called by the inherited one. myBookFactory.clone() will then return a BookFactory instance and not only a Factory instance.
The problem: this BookFactory instance will be cast as Factory, since the type of the function is Factory and not BookFactory.
I'd like to do something like
Public Class Factory
Public Function clone() as Me.GetType()
' Some logic here
' return something
End Function
Public Function all() as List (Of Me.GetType())
' Some logic here
' return something
End Function
End Class
So the returned value would be correctly cast and avoid having to do this each time:
Dim myBookFactory2 = DirectCast(myBookFactory1.clone(), myBookFactory1.getType())
How can I do this?
This seems to be a variation on asking for covariant return types. As you have noticed, this is not supported by VB.NET (or C# for that matter). Typically this is asked in the context of overriding virtual methods, where it is still not allowed. There are several alternatives, each with their own pros and cons.
Use a generic template argument to specify the derived class
This is similar to the way IComparable<T> is most commonly implemented.
Public Class Factory(Of T As Factory)
Public Function Clone() As T
'use GetType(T) to determine derived type
End Function
End Class
Public Class BookFactory
Inherits Factory(Of BookFactory)
End Class
Additionally, if you can add a New constraint to the Factory (eg: Factory(Of T {New, Factory(Of T)})) base class, you may be able to avoid using reflection.
However, this does not prevent the accidental (or potentially malicious) mistake of declaring a class like this:
Public Class EvilFactory
Inherits Factory(Of BookFactory)
'hmmm, now clone will be making the wrong type
End Class
Also, this approach makes it impossible to create a list of factories of different types without resorting to another base class below Factory(Of T) or declaring the list as being of object.
Make new methods on the derived classes that return the specific type you want.
Public Class Factory
Public Function Clone() As Factory
'create derived class, but return as base
End Function
End Class
Public Class BookFactory
Inherits Factory
Public Function CloneBooks() As BookFactory
Return CType(Me.Clone(), BookFactory)
End Function
End Class
This allows you to hide the cast for those times when you know you have a BookFactory and want to get another BookFactory. It also lets you treat all factory types polymorphically in the normal inheritance sense. However, if you have an object typed as Factory, you will still get back an object type as Factory.
Reconsider the inheritance relationship
Depending on how these classes are used, it may not make sense to use the inheritance relationship here. If you are only concerned with not retyping code, you may want to look into code generation instead.
You could potentially use generics to make the problem easier, but it won't remove the requirement to cast at some point. For example:
Public Class Factory(Of T)
Public Function clone() As Factory(Of T)
' Some logic here
' return something
End Function
Public Function all() As Collections.Generic.List(Of T)
' Some logic here
' return something
End Function
End Class
Public Class BookFactory
Inherits Factory(Of Book)
End Class

Return Inherited Generics as Base Generic

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.