简体   繁体   中英

How can I add a method to an interface with the implementing class as argument type in VB.NET?

I want to write an interface that contains a method that has an argument of the same type like the implementing class.

Currently I'm using this:

Public Interface IContent(Of T)
  Function ContentEquals(content As IContent(Of T)) As Boolean
End Interface

Public Class SpecificContent
  Implements IContent(Of Specific)

  Private m_Member As String

  Public Function ContentEquals(content As IContent(Of Specific)) As Boolean Implements IContent(Of Specific).ContentEquals
    ' Actually I need to access content.m_Member here
    ' Of course this doesn't work
    ' since not every IContent(Of Specific) has m_Member
    ' just because SpecificContent does.
    Return True
  End Function
End Class

The problem is, that this interface definition requires the implementation of ContentEquals to accept any object of any type implementing IContent(Of Specific) , not just SpecificContent what I actually want to define.

Is there a way to define a method in the interface I that enforces a method that has an parameter of A for A Implements I and B for B Impements I , ie something like content As MyClass ?

No, there isn't. And frankly, it's a good thing.

Think about it - how would you be able to use such interface? The point of an interface is that you can call certain method regardless of the type of the object that actually implements the interface. If the interface contained a method bound to its implementor's type, in variable of which type would you store that interface? And if you could store it in a variable, how would you pass it to a method that is not aware about the impelementor, only knowing about the interface? How would that method be able to call the interface, what would it pass as a parameter?

For this kind of equality testing you're supposed to override the Object.Equals method, or introduce another overload of Object.Equals that explicitly accepts your own type. You can still use an interface, too, but the parameter will have to be of type Object , just like it is with Object.Equals .

I found out a proper way to achieve what I want. The solution is interface-inheritance and a second generic for the type you want as parameter type. I knew I had done something similar before, but it was in Java and I couldn't quite remember.

These are my interfaces:

Public Interface IContentFactory(Of T, S As IContent(Of T))
  Function CreateFrom(obj As T) As IComparableContent(Of T, S)
End Interface

Public Interface IContent(Of T)
  Sub ApplyTo(obj As T)
End Interface

Public Interface IComparableContent(Of T, S As IContent(Of T))
  Inherits IContent(Of T)
  Function ContentEquals(content As S) As Boolean
End Interface

In my code, I can use these interfaces for a generic content handling class:

Public Class ContentHistory(Of T, S As IContent(Of T))
  Private m_Obj As T
  Private m_Factory As IContentFactory(Of T, S)

  Private m_History As Stack(Of IComparableContent(Of T, S))

  Public Sub New(obj As T, factory As IContentFactory(Of T, S))
    If obj Is Nothing Then Throw New ArgumentNullException("obj")
    If factory Is Nothing Then Throw New ArgumentNullException("factory")

    m_Obj = obj
    m_Factory = factory

    m_History = New Stack(Of IComparableContent(Of T, S))
  End Sub

  Public Sub Backup()
    Dim currentContent = m_Factory.CreateFrom(m_Obj)
    If m_History.Count = 0 OrElse Not m_History.Peek().ContentEquals(currentContent) Then
      m_History.Push(currentContent)
    End If
  End Function

  Private Sub Restore()
    If m_History.Count > 0 Then
      m_History.Pop().ApplyTo(m_Obj)
    End If
  End Function

End Class

Now I can implement specific classes for content-objects and their factories, like so:

Public Class SpecificContentFactory
  Implements IContentFactory(Of Specific, SpecificContent)

  Public Function CreateFrom(obj As Specific) As IComparableContent(Of Specific, SpecificContent) Implements IContentFactory(Of Specific, SpecificContent).CreateFrom
    Return New SpecificContent(obj)
  End Function
End Class

Public Class SpecificContent
  Implements IComparableContent(Of Specific, SpecificContent)

  Private ReadOnly m_Value As String

  Friend Sub New(obj As Specific)
    m_Value = obj.Value
  End Sub

  Public Sub ApplyTo(obj As Specific) Implements IContent(Of Specific).ApplyTo
    obj.Value = m_Value
  End Sub

  Public Function ContentEquals(content As SpecificContent) As Boolean Implements IComparableContent(Of Specific, SpecificContent).ContentEquals
    Return (content.m_Value = m_Value)
  End Function
End Class

To setup this content handler, all I have to do is

Dim obj = New Specific()
Dim history = New ContentHistory(Of Specific, SpecificContent)(obj, New SpecificContentFactory())

and I can use it like

obj.Value = "OldValue"
history.Backup
obj.Value = "OldValue"
history.Backup ' Nothing happens
obj.Value = "NewValue"
history.Backup
obj.Value = "EvenNewerValue"
Dim value = obj.Value ' "EvenNewerValue"
history.Restore
value = obj.Value ' "NewValue"
history.Restore
value = obj.Value ' "OldValue"
history.Restore ' Nothing happens
value = obj.Value ' "OldValue"

As long as I provide the SpecificContent and SpecificContentFactory implementations, I can use the ContentHistory(Of Specific, SpecificContent) for any type as Specific that I like.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM