Расширить тип generi c на унаследованный интерфейс - PullRequest
0 голосов
/ 07 апреля 2020

Я надеюсь, что не повторяю вопрос, который уже решен, но у меня были проблемы, чтобы найти решение для моей проблемы в подобных вопросах. Я нашел эту топи c на форуме: Приведение объекта к двум интерфейсам одновременно, чтобы вызвать обобщенный c метод . Честно говоря, мой C# очень плох, так как я кодирую только на VB. NET, и я думаю, что моя проблема немного другая (унаследованные интерфейсы вместо автономных).

Моя проблема также связана с Сериализация (XML), для которой моя реализация обобщений не дает исключение. Дело в том, что я должен привести мой обобщенный тип c, который сериализатор должен десериализовать, к интерфейсу, что приведет к исключению. Чтобы лучше объяснить, см. Упрощенный пример кода ниже:

    Public Interface IParent
       Property ParentProp As String
    End Interface

    Public Interface IChild
       Inherits IParent
       Property ChildProp As String
    End Interface

    Public Class ExampleClass
       Implements IChild

       Public Property ChildProp As String = "Child Property" Implements IChild.ChildProp
       Public Property ParentProp As String = "Parent Property" Implements IParent.ParentProp
    End Class

    Public Class ExampleListClass
       Inherits List(Of Integer)
       Implements IChild

       Public Property ChildProp As String = "List Child Property" Implements IChild.ChildProp
       Public Property ParentProp As String = "List Parent Property" Implements IParent.ParentProp
    End Class

    Public Class TestEnv
       Public Shared Sub Main()
          Dim str As String

          Dim locExampleListClass = New ExampleListClass
          str = TestEnv.Method1(Of Integer, ExampleListClass)(locExampleListClass)
          MessageBox.Show(str, "locExampleListClass", MessageBoxButtons.OK, MessageBoxIcon.Information)

          Dim locExampleClass = New ExampleClass
          str = TestEnv.Method2(locExampleClass)
          str = TestEnv.Method2_Dirty(locExampleClass)
          MessageBox.Show(str, "locExampleClass", MessageBoxButtons.OK, MessageBoxIcon.Information)
       End Sub

       Public Shared Function Method1(Of T, C As {IList(Of T), IParent})(ByRef Instance As C) As String
          If TypeOf (Instance) Is IChild Then
             'Since instance is of type C which is restricted to IParent, the passed instance is a list which implements IChild (and of course IParent - because IChild inherits IParent)
             Dim CastedInstance = DirectCast(Instance, IChild)
             '!-----------------PseudoCode-----------------!
             'Dim CastedInstance = DirectCast(Instance, {C, IChild})
             Dim ReturnVal = TestEnv.Method2(CastedInstance)
             Dim fake = TestEnv.Method2_Dirty(Instance)

             Return ReturnVal
          Else
             'Something else is done (e.g. deactivating context menues only useful for IChild)
             Return Instance.ParentProp
          End If
          '------------------------------------------------------------------------------------------
       End Function

       Public Shared Function Method2(Of T As {IChild})(ByRef Instance As T) As String
          Return Instance.ChildProp

          'Note: Xml-serialization into T when T is an interface seems not possible. Thus type C in Method1 needs to be maintained but extended to IChild
       End Function

       Public Shared Function Method2_Dirty(Of T As {IParent})(ByRef Instance As T) As String
          'This would work but is not very nice (there is a reason why T shall be restricted to IChild in the first place - no ifs or trycasts needed)
          If TypeOf (Instance) Is IChild Then
             Return DirectCast(Instance, IChild).ChildProp
          Else
             Throw New Exception("The input parameter needs to be of type IChild but I am too stupid to make it work")
          End If
       End Function
    End Class

Итак, есть два примера классов, один из которых является списком и очень прост. Оба реализуют интерфейс IChild. Метод 1, однако, ограничивает входной аргумент только IParent и проверяет, реализован ли IChild. Если не что-то еще сделано. Если да, то экземпляр может быть брошен без проблем. Таким образом, на данный момент я знаю, что экземпляр реализует IList (из T) и IChild. Теперь Method2 может быть вызван с приведенным экземпляром. Все это, очевидно, компилируется и работает. Моя проблема в том, что Method2 в моем случае является десериализатором (пытается указать это, передав Instance ByRef). Поскольку CastedInstance имеет тип IChild, десериализатор выдает исключение.

Таким образом, мне по-прежнему нужен тип C, но расширенный IChild. После оператора if в Method1 я знаю, что экземпляр полностью выполняет ограничения, но мне не удается реализовать правильный код (см. Комментарий PseudeCode). Я думаю, я мог бы реализовать Method2_Dirty, который ограничивает только IParent и делать проверки типов и trycasts. Тем не менее, это кажется не очень хорошим, поскольку с тех пор исключения генерируются во время выполнения, а не во время кодирования перед компиляцией.

Как уже было сказано в начале, я надеюсь, что не буду повторять какие-либо вопросы и жду ваших отзывов. Спасибо

1 Ответ

0 голосов
/ 11 апреля 2020

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

Я понял, что моя проблема на самом деле связана с сериализацией XML (с двоичной сериализацией исключений не происходит). Спасибо Айбе за то, что подтолкнул меня в этом направлении. Я думаю, что проблему можно решить с помощью класса DataContractSerializer. Однако я реализовал кое-что еще, что ближе к обобщенному c topi c этого поста.

Ниже приведено расширение исходного примера, которое более четко решает проблему (я надеюсь). Это включает де / сериализацию сейчас, чтобы показать, какова реальная проблема. Извините, что он довольно длинный импортировать снова

Дело в том, что после импорта что-то должно быть сделано с импортированным экземпляром (в этом примере изменяется только строка ChildProperty)

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

Первая версия ImportFromFile создает проблемы для Xml Сериализация, вот почему я создал этот пост (было бы неплохо, если бы мы могли как-то расширить generi c type C, поскольку мы знаем, что он будет работать - см. проверку TypeOf в CheckImportFromFile)

Вторая версия ImportFromFile_RuntimeChecks использует проверки во время выполнения, которые были бы возможны, но, очевидно, не слишком хороши для программиста (без признаков того, что требуется методу во время кодирования) В окончательной версии ImportFromFile_Final требуется дополнительная функция входных параметров, который сравнивает импортируемый экземпляр с требуемым типом, чтобы изменить свойство Child. Я считаю это решение достаточным, потому что таким образом программист (наиболее вероятно, я) осознает, что это приведение должно работать, поэтому интерфейс должен быть реализован в классе.

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

...