首页 > 解决方案 > 在没有重复成员的情况下实现通用和非通用接口

问题描述

前提

Enum这样:

Public Enum Foo
    A
    B
    C
End Enum

我想创建一个类来跟踪Foo内部存在哪些值HashSet(Of Foo)并将数据公开为IDictionary(Of Foo, Boolean). 该类还需要实现非泛型IDictionary接口(这是我的用例所要求的)。

Public Class FooTracker
    Implements IDictionary, IDictionary(Of Foo, Boolean)

    Private foos As HashSet(Of Foo)

    ...

End Class

FooTrackerwhere fooscontains only的期望行为BFooTracker(Foo.A)并且FooTracker(Foo.C)应该在返回False时都FooTracker(Foo.B)返回True

问题

IDictionary<TKey, TValue>并且IDictionary具有应该由同一成员合理实现FooTracker但具有不同签名的成员。我们以Item房产为例。

IDictionary想要Default Public Property Item(key As Object) As Object Implements IDictionary.Item
IDictionary<Foo, Boolean>想要Default Public Property Item(key As Foo) As Boolean Implements IDictionary(Of Foo, Boolean).Item

我不想实现两个单独的属性,一个只调用另一个,因为那是很多无用的样板代码。更重要的是,我不希望泛型和非泛型版本都显示为重载。

看一下标准Dictionary(TKey, TValue)类(不是接口)。当您Item在 Visual Studio 中访问该属性时,您获得的唯一选项是通用Item(key as TKey). 尽管实现了非泛型,但它不会向您显示Item(key as Object)事件。但它是如何做到的呢?他们如何只有一个成员同时实现and ?Dictionary(TKey, TValue)IDictionaryIDictionary.ItemIDictioanry<TKey, TValue>.Item

标签: .netvb.netgenericsinterface

解决方案


我想到了。我缺少的是我不知道您可以将接口成员实现为Private,这可以防止通过实现类型调用它,但仍然允许通过接口类型调用它。

对于遇到同样问题的任何人,这里是使用我的示例的完整示例实现FooTracker。请注意,这是一个固定大小字典的示例,这就是为什么所有AddRemove方法都被隐藏并在调用时抛出异常的原因。

Public Class FooTracker
    Implements IDictionary, IDictionary(Of Foo, Boolean)

    Private foos As HashSet(Of Foo)

    Private Shared allFooValues As New ReadOnlyCollection(Of Foo)([Enum].GetValues(GetType(Foo)))

    Default Public Property Item(key As Foo) As Boolean Implements IDictionary(Of Foo, Boolean).Item
        Get
            Return foos.Contains(key)
        End Get
        Set(value As Boolean)
            If value Then
                foos.Add(key)
            Else
                foos.Remove(key)
            End If
        End Set
    End Property

    Private Property IDictionary_Item(key As Object) As Object Implements IDictionary.Item
        Get
            Return Item(key)
        End Get
        Set(value As Object)
            Item(key) = value
        End Set
    End Property

    Public ReadOnly Property Keys As ICollection(Of Foo) Implements IDictionary(Of Foo, Boolean).Keys
        Get
            Return allFooValues
        End Get
    End Property

    Private ReadOnly Property IDictionary_Keys As ICollection Implements IDictionary.Keys
        Get
            Return Keys
        End Get
    End Property

    Public ReadOnly Property Values As ICollection(Of Boolean) Implements IDictionary(Of Foo, Boolean).Values
        Get
            Return allFooValues.Select(Function(F) Item(F))
        End Get
    End Property

    Private ReadOnly Property IDictionary_Values As ICollection Implements IDictionary.Values
        Get
            Return Values
        End Get
    End Property

    Public ReadOnly Property IsReadOnly As Boolean Implements IDictionary.IsReadOnly, ICollection(Of KeyValuePair(Of Foo, Boolean)).IsReadOnly
        Get
            Return False
        End Get
    End Property

    Public ReadOnly Property IsFixedSize As Boolean Implements IDictionary.IsFixedSize
        Get
            Return True
        End Get
    End Property

    Private Sub ThrowNotSupportedException()
        Throw New NotSupportedException()
    End Sub

    Public ReadOnly Property Count As Integer Implements ICollection.Count, ICollection(Of KeyValuePair(Of Foo, Boolean)).Count
        Get
            Return allFooValues.Count
        End Get
    End Property

    Public ReadOnly Property SyncRoot As New Object Implements ICollection.SyncRoot

    Public ReadOnly Property IsSynchronized As Boolean Implements ICollection.IsSynchronized
        Get
            Return False
        End Get
    End Property

    Private Sub Add(key As Foo, value As Boolean) Implements IDictionary(Of Foo, Boolean).Add
        ThrowNotSupportedException()
    End Sub

    Private Sub IDictionary_Add(key As Object, value As Object) Implements IDictionary.Add
        ThrowNotSupportedException()
    End Sub

    Private Sub ICollection_Add(item As KeyValuePair(Of Foo, Boolean)) Implements ICollection(Of KeyValuePair(Of Foo, Boolean)).Add
        ThrowNotSupportedException()
    End Sub

    Public Sub Clear() Implements IDictionary.Clear
        foos.Clear()
    End Sub

    Private Sub ICollection_Clear() Implements ICollection(Of KeyValuePair(Of Foo, Boolean)).Clear
        Clear()
    End Sub

    Private Function Remove(key As Foo) As Boolean Implements IDictionary(Of Foo, Boolean).Remove
        ThrowNotSupportedException()
    End Function

    Private Sub IDictionary_Remove(key As Object) Implements IDictionary.Remove
        ThrowNotSupportedException()
    End Sub

    Private Function ICollection_Remove(item As KeyValuePair(Of Foo, Boolean)) As Boolean Implements ICollection(Of KeyValuePair(Of Foo, Boolean)).Remove
        ThrowNotSupportedException()
    End Function

    Public Sub CopyTo(array() As KeyValuePair(Of Foo, Boolean), arrayIndex As Integer) Implements ICollection(Of KeyValuePair(Of Foo, Boolean)).CopyTo
        For i As Integer = 0 To allFooValues.Count - 1
            Dim F = allFooValues(i)
            array(arrayIndex + i) = New KeyValuePair(Of Foo, Boolean)(F, Item(F))
        Next
    End Sub

    Private Sub ICollection_CopyTo(array As Array, index As Integer) Implements ICollection.CopyTo
        CopyTo(array, index)
    End Sub

    Public Function Contains(item As KeyValuePair(Of Foo, Boolean)) As Boolean Implements ICollection(Of KeyValuePair(Of Foo, Boolean)).Contains
        Return Me.Item(item.Key) = item.Value
    End Function

    Private Function IDictionary_Contains(key As Object) As Boolean Implements IDictionary.Contains
        Return Contains(key)
    End Function

    Public Function ContainsKey(key As Foo) As Boolean Implements IDictionary(Of Foo, Boolean).ContainsKey
        Return allFooValues.Contains(key)
    End Function

    Public Function TryGetValue(key As Foo, ByRef value As Boolean) As Boolean Implements IDictionary(Of Foo, Boolean).TryGetValue
        value = Item(key)
        Return True
    End Function

    Public Function GetEnumerator() As Enumerator
        Return New Enumerator(Me)
    End Function

    Private Function IDictionary_GetEnumerator() As IDictionaryEnumerator Implements IDictionary.GetEnumerator
        Return GetEnumerator()
    End Function

    Private Function IEnumerable_GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
        Return GetEnumerator()
    End Function

    Private Function IEnumerable2_GetEnumerator() As IEnumerator(Of KeyValuePair(Of Foo, Boolean)) Implements IEnumerable(Of KeyValuePair(Of Foo, Boolean)).GetEnumerator
        Return GetEnumerator()
    End Function

    Public Class Enumerator
        Implements IEnumerator(Of KeyValuePair(Of Foo, Boolean)), IDictionaryEnumerator

        Private tracker As FooTracker
        Private nextIndex As Integer
        Private currentItem As KeyValuePair(Of Foo, Boolean)

        Friend Sub New(tracker As FooTracker)
            Me.tracker = tracker
        End Sub

        Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext
            If nextIndex < allFooValues.Count Then
                Dim F = allFooValues(nextIndex)
                _Current = New KeyValuePair(Of Foo, Boolean)(F, tracker(F))
                nextIndex += 1
                Return True
            Else
                _Current = New KeyValuePair(Of Foo, Boolean)()
                Return False
            End If
        End Function

        Public ReadOnly Property Current As KeyValuePair(Of Foo, Boolean) Implements IEnumerator(Of KeyValuePair(Of Foo, Boolean)).Current

        Private ReadOnly Property IEnumerator_Current As Object Implements IEnumerator.Current
            Get
                Return Current
            End Get
        End Property

        Public Sub Reset() Implements IEnumerator.Reset
            nextIndex = 0
            _Current = New KeyValuePair(Of Foo, Boolean)()
        End Sub

        Public ReadOnly Property Key As Object Implements IDictionaryEnumerator.Key
            Get
                Return Current.Key
            End Get
        End Property

        Public ReadOnly Property Value As Object Implements IDictionaryEnumerator.Value
            Get
                Return Current.Value
            End Get
        End Property

        Public ReadOnly Property Entry As DictionaryEntry Implements IDictionaryEnumerator.Entry
            Get
                Return New DictionaryEntry(Current.Key, Current.Value)
            End Get
        End Property

        Private Sub Dispose() Implements IDisposable.Dispose
        End Sub
    End Class

End Class

如果您声明一个 type 变量FooTracker,则只有泛型Item属性是可访问的。但是您可以强制FooTracker转换为IDictionary,这允许您访问非泛型Item属性。


推荐阅读