Хранение объекта, который реализует несколько интерфейсов и является производным от определенной базы (.net) - PullRequest
4 голосов
/ 07 декабря 2010

В .net можно использовать обобщенные значения, чтобы функция могла принимать аргументы, которые поддерживают один или несколько интерфейсов, и являются производными от базового типа, даже если не существует какого-либо единственного типа, из которого происходят все действительные типы аргументов.Например, можно сказать:

Sub Foo(Of T As {IInterface1, IInterface2, SomeBaseType})(Param as T)

и ему разрешено передавать любую производную SomeBaseType, которая реализует как IInterface1, так и IInterface2.Это будет работать, даже если SomeBaseType не поддерживает Interface1 и Interface2, а классы, которые реализуют эти интерфейсы, не имеют общего предка, который также реализует их.

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

Самое близкое, что я смог придумать, - это определить интерфейс INest (возможно, не лучшее имя - кто-нибудь может его улучшить?), Таким образом:

Interface INest(Of Out T)
    Function Nest() As T
End Interface

И для любого интерфейса, который будет использоваться в сочетании с другими или с «ограничением» базового класса, определите универсальную версию, как показано ниже

Interface IFun1
    ' Any members of the interface go here, e.g. ...'
    Sub DoFun1()
End Interface

Interface IFun1(Of Out T)
    ' This one does nothing but inherit'
    Inherits IFun1, INest(Of T)
End Interface

Класс, который будет поддерживать несколькоинтерфейсы должны объявлять себя как реализующие универсальные, с самим собой в качестве аргумента типа.

Class test123a
    Inherits sampleBase
    Implements IFun1(Of test123a), IFun2(Of test123a), IFun3(Of test123a)
End Class

Если это сделано, можно определить аргумент функции или переменную класса, которая таким образом поддерживает несколько ограничений:

Dim SomeField as IFun1(Of IFun2(Of IFun3(Of sampleBase)))

и затем назначьте ему любой класс, производный от sampleBase, который реализует эти интерфейсы.SomeField будет реализовывать IFun1;SomeField.Nest будет реализовывать IFun2;SomeField.Nest.Nest будет реализовывать IFun3.Обратите внимание, что не требуется, чтобы IFun1, IFun2, IFun3 или sampleBase совместно использовали какие-либо общие производные, кроме универсальных интерфейсов, наследуемых от INest (Of T).Также обратите внимание, что независимо от того, сколько производных от INest интерфейсов реализует класс, ему нужно только определить одну реализацию INest (Of T) .Nest.

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

Есть ли лучший способ хранить что-либо таким образом, чтобы это «стало известно»поддержка нескольких интерфейсов и наследование от определенного базового типа?Учитывая, что можно написать такой код безопасным для типов способом, может показаться, что .net 2.0 CLR, вероятно, вполне может поддерживать такую ​​вещь, если компиляторы предложат небольшую помощь.Я не знаю ни одного особенно приятного подхода с существующими компиляторами.

1 Ответ

2 голосов
/ 17 февраля 2011

Лучший способ, который я могу придумать, - это сделать абстрактное хранилище и реализовать его в общем виде.Например (извините мой VB.NET):

MustInherit Class Storage
    Public MustOverride Sub DoSomething()
End Class

Class Storage(Of T As {IInterface1, IInterface2, SomeBaseType})
    Inherits Storage

    Public Overrides Sub DoSomething()
        ' do something with Value.
    End Sub

    Public Value As T
End Class

И использование

Dim S As Storage

Sub Foo(Of T As {IInterface1, IInterface2, SomeBaseType})(ByVal Param As T)
    S = New Storage(Of T) With {.Value = Param}
End Sub

Sub UseS()
    S.DoSomething();
End Sub

Обновление: Хорошо, потому что мы не можем заранее определить всеиз действий:

MustInherit Class Storage
    MustOverride ReadOnly Property SomeBaseType As SomeBaseType
    MustOverride ReadOnly Property IInterface1 As IInterface1
    MustOverride ReadOnly Property IInterface2 As IInterface2
End Class

Class Storage(Of T As {IInterface1, IInterface2, SomeBaseType})
    Inherits Storage

    Public Value As T

    Public Overrides ReadOnly Property IInterface1 As IInterface1
        Get
            Return Value
        End Get
    End Property

    Public Overrides ReadOnly Property IInterface2 As IInterface2
        Get
            Return Value
        End Get
    End Property

    Public Overrides ReadOnly Property SomeBaseType As SomeBaseType
        Get
            Return Value
        End Get
    End Property
End Class
...