vb.net специализированные/перегруженные дженерики

Я склонен ненавидеть повторения в коде, поэтому, когда я сталкиваюсь с проблемой, в которой различаются только типы, я склонен использовать дженерики. Исходя из фона C ++, я нахожу версию vb.net довольно разочаровывающей, я знаю, что C ++ имеет специализацию шаблонов, и я предполагаю, что vb.net не имеет, поэтому у меня есть набор подпрограмм, которые выполняют один и тот же код независимо от передаваемого типа .

что-то вроде этого

Public Sub decision(Of T)(ByVal a As T, ByVal b As Integer)
  If b > 10 then
    gt(a)
  Else
    lt(a)
  End If
End Sub

Я всегда передаю в подпрограмму только два типа: строки и целые числа, и то, что эти подпрограммы делают со строками, отличается от того, что они делают с целыми числами.

Public Sub gt(ByVal a As String)
Public Sub gt(ByVal a As Integer)

Public Sub lt(ByVal a As String)
Public Sub lt(ByVal a As Integer)

Вот где я разочаровываюсь в том, что vb.net vs C++, AFAIK, C++ будет проверять типы во время компиляции и только против того, какие типы отправляются на решение. Однако в vb.net я получаю сообщение об ошибке, что тип T не может быть преобразован в String или Integer.

Error   3   Overload resolution failed because no accessible 'gt' can be called with these arguments:
    'Public Sub gt(a As String)': Value of type 'T' cannot be converted to 'String'.
    'Public Sub gt(a As Integer)': Value of type 'T' cannot be converted to 'Integer'.

Я пробовал ограничения Public Sub decision(Of T As {String, Integer})(ByVal a As T, ByVal b As Integer), но ограничения должны быть наследуемыми классами, поэтому нельзя использовать ни String, ни Integer.

Моим следующим решением было добавить общие версии gt и lt:

Public Sub lt(Of T)(ByVal a As T)
  Debug.Fail("Not Implemented")
End Sub

Public Sub lt(Of T)(ByVal a As T)
  Debug.Fail("Not Implemented")
End Sub

И эй! больше нет ошибок компиляции, однако единственная вызываемая подпрограмма - это общая версия gt и lt. Что, я думаю, имеет смысл в свете предыдущего не может преобразовать ошибки. Я сталкивался с этой проблемой раньше, когда были неуниверсальные перегрузки универсальной подпрограммы, я не мог найти решение тогда и не могу найти решение сейчас.

Есть ли что-то, что мне не хватает, что сделало бы возможным этот тип перегрузки?

Изменить: полный рабочий пример

Module Module1
   Sub Main()

      decision(1, 5)
      decision(1, 10)
      decision("hello world", 5)
      decision("hello world", 10)

   End Sub


   Public Sub decision(Of T)(ByVal a As T, ByVal b As Integer)
      If b > 10 Then
         gt(a)
      Else
         lt(a)
      End If
   End Sub

   Public Sub gt(ByVal a As String)
      Debug.WriteLine(" gt string:  " + a)
   End Sub
   Public Sub gt(ByVal a As Integer)
      Debug.WriteLine(" gt integer: " + a.ToString)
   End Sub

   Public Sub lt(ByVal a As String)
      Debug.WriteLine(" lt string: " + a)
   End Sub
   Public Sub lt(ByVal a As Integer)
      Debug.WriteLine(" lt integer: " + a.ToString)
   End Sub

#If False Then
   Public Sub gt(Of T)(ByVal a As T)
      Debug.Fail("Not implemented")
   End Sub
   Public Sub lt(Of T)(ByVal a As T)
      Debug.Fail("Not implemented")
   End Sub
#End If
End Module

person Apeiron    schedule 26.01.2012    source источник
comment
Проблема заключается не в вашем объявлении decision, а в вызывающем методе или классе - пожалуйста, покажите нам это. Ваша ситуация не требует дженериков, перегрузка, которую вы показали изначально, была в порядке.   -  person Ry-♦    schedule 26.01.2012
comment
добавлен рабочий пример, демонстрирующий проблему, прикрепленный код как есть будет иметь ошибку невозможности преобразования, изменение #if False Then на #If True Then всегда будет вызывать общую версию.   -  person Apeiron    schedule 26.01.2012
comment
Да, я это понимаю, но это тоже не проблема. Можете ли вы привести какой-нибудь настоящий пример?   -  person Ry-♦    schedule 26.01.2012
comment
к сожалению нет. Мне не нравится публиковать код компании, хотя я знаю, что это не причинит никакого вреда, я просто никогда не хочу рисковать. Думаю, я не уверен, что еще нужно, поскольку приведенный выше код имеет ту же проблему, с которой я сталкиваюсь в другом месте, возможно, если вы дадите мне знать, что вы ищете, я могу предоставить дополнительную информацию. С тех пор я придумал другой способ решения моей проблемы, но поскольку я дважды сталкивался с этой проблемой, меня больше интересует приведенный пример, поэтому я знаю, как решить эту проблему в будущем. У меня есть версия приведенного выше кода на С++, которая отлично работает.   -  person Apeiron    schedule 27.01.2012
comment
Ну... такой ситуации никогда не должно быть. Если у вас есть другое исправление, это нормально. Но я не могу помочь без фактического метода и вызывающего метода.   -  person Ry-♦    schedule 27.01.2012
comment
Я предполагаю, что моя главная мысль будет заключаться в следующем: я мог бы сделать это на C++, есть ли VB.net способ сделать что-то подобное. Даже если вы не можете, я очень ценю, что вы нашли время, чтобы прочитать вопрос и ответить на комментарии.   -  person Apeiron    schedule 27.01.2012


Ответы (1)


Из Различия между шаблонами C++ и универсальными шаблонами C# (то же относится и к VB . СЕТЬ):

C++ допускает код, который может быть недопустимым для всех параметров типа в шаблоне, который затем проверяется на наличие определенного типа, используемого в качестве параметра типа. C# требует, чтобы код в классе был написан таким образом, чтобы он работал с любым типом, удовлетворяющим ограничениям. Например, на C++ можно написать функцию, использующую арифметические операторы + и - над объектами параметра типа, что выдаст ошибку в момент инстанцирования шаблона с типом, не поддерживающим эти операторы. С# запрещает это; разрешены только те языковые конструкции, которые можно вывести из ограничений.

Я не могу решить вашу проблему с .NET Generics. Но вы можете избежать повторения логики, используя лямбда-выражения и замыкания, что я также считаю более естественным способом сделать это в .NET:

Public Sub Decision(ByVal a As String, ByVal b As Integer)
    Decision(b, Sub() gt(a), Sub() lt(a))
End Sub

Public Sub Decision(ByVal a As Integer, ByVal b As Integer)
    Decision(b, Sub() gt(a), Sub() lt(a))
End Sub

Private Sub decision(ByVal b As Integer, ByVal gt As Action, ByVal lt As Action)
    If b > 10 Then
        gt()
    Else
        lt()
    End If
End Sub

Public Sub gt(ByVal a As String)
    Debug.WriteLine(" gt string:  " + a)
End Sub
Public Sub gt(ByVal a As Integer)
    Debug.WriteLine(" gt integer: " + a.ToString)
End Sub

Public Sub lt(ByVal a As String)
    Debug.WriteLine(" lt string: " + a)
End Sub
Public Sub lt(ByVal a As Integer)
    Debug.WriteLine(" lt integer: " + a.ToString)
End Sub 
person tore    schedule 27.01.2012
comment
Что ж, причина, по которой я использовал дженерики, заключалась в том, чтобы уменьшить количество дублированного кода, в частности кода логического ветвления. Похоже, это будет лучшее, что я смогу сделать. В какой версии .NET были представлены лямбда-выражения? - person Apeiron; 03.03.2012
comment
Лямбды были представлены в VB 9/VS2008, который был выпущен с .NET 3.5. Однако можно установить целевую платформу .NET 2.0 (как описано здесь: stackoverflow.com/a/1162235/953288). - person tore; 03.03.2012
comment
Хороший! Я никогда не думал об использовании лямбда-выражений для этой цели. Что бы это ни стоило, альтернатива, которая, вероятно, близка к тому, что происходит с C ++ во время выполнения, заключается в том, чтобы полагаться на общий и ветвление в зависимости от параметра типа (используйте GetType(T) через отражение). Однако вы теряете защиту строгой типизации, а также производительность (отражение считается жадным). - person Ama; 05.04.2020