The problem with generics, interfaces and casting

I recently added an interface for some user controls that I implemented. The interface is pretty simple. It has one method supporting the chain:

Public Interface IMyInterface(Of T As WebControl) Function DoSomething() As T End Interface 

The implementations are also quite simple:

 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. Problems arise when I try to loop over a set of controls that implement the IMyInterface interface, for example:

 Dim myList = New List(Of IMyInterface(Of WebControl)) myList.Add(someCustomControl) myList.ForEach(Sub(i) i.DoSomething()) 

someCustomControl is a MyCustomControl that implements IMyInterface(Of MyCustomControl) instead of IMyInterface(Of WebControl) .

I get this error in the second line (where I try to add someCustomControl ):

The Strict On parameter disables implicit conversions from "MyCustomControl" to "IMyInterface (Of WebControl)".

Is there any way around this error? I'm close to having it working, but I don't know enough about generics to go beyond that point.

+6
source share
3 answers

Covariance is a language feature that was introduced in VS 2010 and solves your problem. You must define your generic type so that type T 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 use covariance. It allows you to use generics of a more derived type instead of a generic type with a base type. Thus, in your case, it will support the IMyInterface(Of MyCustomControl)) object in those places where the IMyInterface(Of WebControl)) code is usually expected IMyInterface(Of WebControl)) , for example, your for loop.

Note that covariance has a limitation. The covariant type T can only be used as the return value of a function, and not as a parameter to a function (or auxiliary). For example, if the DoSomething signature in IMyInterface looks like this, the compiler will 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 specified restriction is a problem.

Additional Information on MSDN:

+4
source

I don't know what your DoSomething function does, but I'm trying to assign an instance of CssClass there for testing purposes.

Declare the interface as follows:

 Public Interface IMyInterface(Of Out T As WebControl) Function DoSomething() As T End Interface 

Note the Out T parameter.

Create two 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 the test page PageLoad:

 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 />") 

As a result, the CssClass property of both someCustomControl and someCustomControl2 is set to the appropriate values.

This indicates that the DoSomething interface function was successfully called and the instance has changed.

+1
source

You will need to pass the object before adding it:

 myList.Add(CType(someCustomControl, IMyInterface(Of WebControl))) 

You may also want to ensure that the interface is not shared, and your DoWork method returns as the interface itself.

 Public Interface IMyInterface Function DoSomething() As IMyInterface End Interface 

When you need to specify a type in an interface definition, it distracts from the power of the interfaces (it is not necessary to know about the implementation).

0
source

Source: https://habr.com/ru/post/946989/


All Articles