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.
As mentioned previously, generics can eliminate the requirement for casting.
Implementing generics into your code can greatly improve its overall quality by preventing unprecedented runtime errors involving data types and typecasting.
A generic interface is primarily a normal interface like any other. It can be used to declare a variable but assigned the appropriate class. It can be returned from a method. It can be passed as argument. You pass a generic interface primarily the same way you would an interface.
Cannot Use Casts or instanceof With Parameterized Types. Cannot Create Arrays of Parameterized Types. Cannot Create, Catch, or Throw Objects of Parameterized Types. Cannot Overload a Method Where the Formal Parameter Types of Each Overload Erase to the Same Raw Type.
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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With