2013-06-10 5 views
6

Недавно я добавил интерфейс к некоторым пользовательским элементам управления, которые я реализовал. Интерфейс довольно простой. Он имеет один метод, который поддерживает формирования цепочки:Проблема с дженериками, интерфейсами и литьем

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

Реализации также является довольно простым:

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 

Все отлично работает на эту точку. Проблемы возникают при попытке перебрать коллекцию элементов управления, которые все реализуют интерфейс IMyInterface, например, так:

Dim myList = New List(Of IMyInterface(Of WebControl)) 

myList.Add(someCustomControl) 

myList.ForEach(Sub(i) i.DoSomething()) 

someCustomControl является MyCustomControl, который реализует IMyInterface(Of MyCustomControl) вместо IMyInterface(Of WebControl).

Я получаю эту ошибку на второй линии (где я пытаюсь добавить someCustomControl):

Option Strict On запрещает неявные преобразования из 'MyCustomControl' до 'IMyInterface (Of WebControl).

Есть ли способ обойти эту ошибку? Я близок к тому, что он работает, но я не знаю достаточно о дженериках, чтобы выйти за пределы этого момента.

+0

В то время как вы пытаетесь объявить myList как 'Dim myList = New List (Of IMyInterface (Of WebControl)), попробуйте' Dim myList = Новый список (Of Object) 'или что-то в этом роде. – Vishal

+0

@Vishal Я думаю, что без типа 'IMyInterface' вы не могли бы вызывать' DoSomething() 'для объектов в цикле' ForEach'. – jbabey

+0

Извините, я этого не знал. – Vishal

ответ

4

Covariance - это языковая функция, которая была представлена ​​в VS 2010 и решает вашу проблему. Вы должны определить вашу общий, что тип T имеет Out ключевое слово перед ним:

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

При использовании Out ключевого слова, вы используете ковариации. Он позволяет использовать генерики более производного типа вместо общего типа с базовым типом. Таким образом, в вашем случае он позволит объекту IMyInterface(Of MyCustomControl)) в местах, где обычно ожидается код IMyInterface(Of WebControl)), например, ваш цикл for.

Обратите внимание, что ковариация имеет ограничение. Ковариантный тип T может использоваться только как возвращаемое значение функции, а не как параметр в функцию (или вспомогательную). Например, если DoSomething подпись в IMyInterface Выглядело это компилятор будет жаловаться:

' Here the type T is used as an input param - compiler error 
Sub DoSomething(ByVal sampleArg As T) 

Учитывая ваш сценарий СЦЕПЛЕНИЕ, я не думаю, что такое ограничение является проблемой.

Дополнительная информация на сайте MSDN:

+0

Это именно то, что я искал. Спасибо. – jbabey

0

Вам нужно будет преобразовать объект, прежде чем добавить:

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

Вы также можете рассмотривать сделать интерфейс не является универсальным и ваш «DoWork» метод возвратного типа, как и сам интерфейс.

Public Interface IMyInterface 
    Function DoSomething() As IMyInterface 
End Interface 

Когда вы должны указать тип в определении интерфейса это своего рода отнимает от силы интерфейсов (не зная о реализации).

+0

Я бы хотел, чтобы он работал, не нарушая функциональность цепочки. – jbabey

+0

Если интерфейс, возвращающийся сам, не удовлетворяет вашим функциям цепочки, тогда интерфейс должен быть переработан или не использовать интерфейсы. В качестве альтернативы вы можете написать базовый класс 'WebControl', который делает то же самое. – Jay

1

Я не знаю, что ваша функция DoSomething делает, но я пытаюсь назначить CssClass экземпляра, там для целей тестирования.

Объявляет интерфейс следующим образом:

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

Уведомление параметр Out T.

Создайте 2 управления, которые реализуют интерфейс:

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 

на тестовой странице 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 />") 

Результата есть CssClass свойства как someCustomControl & someCustomControl2 устанавливается в соответствующем значения.

Это показывает, что функция интерфейса DoSomething была успешно вызвана и экземпляр был изменен.

+0

'Out' - это то, чего мне не хватало. Спасибо. – jbabey

Смежные вопросы