如何创建与嵌套类型参数的子类一起使用的扩展方法?
How to create an extension method that works with a subclass of a nested type parameter?
我在访问我创建的扩展方法时遇到问题,该扩展方法采用基数为 class 的对象作为其类型参数。
这是基地 class:
Namespace Models.Db
Public Class Base
Public Property Id As Integer
End Class
End Namespace
...和子class:
Namespace Models.Db
Public Class City
Inherits Base
...
End Class
End Namespace
扩展方法如下:
Imports System.Collections.Generic
Imports System.Data.Entity
Imports System.Linq
Imports System.Runtime.CompilerServices
Imports Matrix.Models
Imports Moq
Public Module Extensions
<Extension>
Public Sub SetupMock(Instance As Mock(Of DbSet(Of Db.Base)), Entities As List(Of Db.Base))
Instance.As(Of IQueryable(Of Db.Base)).Setup(Function(Cities) Cities.GetEnumerator).Returns(Entities.GetEnumerator)
Instance.As(Of IQueryable(Of Db.Base)).Setup(Function(Cities) Cities.ElementType).Returns(Entities.AsQueryable.ElementType)
Instance.As(Of IQueryable(Of Db.Base)).Setup(Function(Cities) Cities.Expression).Returns(Entities.AsQueryable.Expression)
Instance.As(Of IQueryable(Of Db.Base)).Setup(Function(Cities) Cities.Provider).Returns(Entities.AsQueryable.Provider)
End Sub
End Module
...这是调用代码:
Private Function GetDbContextMock() As Mock(Of Db.Context)
Dim oCitiesMock As Mock(Of DbSet(Of Db.City))
oCitiesMock = New Mock(Of DbSet(Of Db.City))
oCitiesMock.SetupMock(Me.Cities) ' Me.Cities is a List(Of Db.City)
End Function
编译时错误为:
'SetupMock' is not a member of 'Mock(Of DbSet(Of City))'
但是,当我将调用代码更改为:
Dim oCitiesMock As Mock(Of DbSet(Of Db.Base))
oCitiesMock = New Mock(Of DbSet(Of Db.Base))
...找到扩展方法。但是我需要向扩展方法发送一个 subclass 引用,而不是一个 base class 引用。
This answer 表示可以使用简单的 superclass/subclass 对象,但由于这是处理类型参数,所以有点复杂。
This answer 乍一看似乎可以提供解决方案,但经过进一步思考,它只解决了一个级别的类型参数。上面的代码有两个。我不确定这种细微差别是否会有所不同,但最终代码无法编译。
这可以在 .NET 中完成吗?
您的第二个链接答案是正确的。额外的 'level' 打字并不重要。该答案的关键点是在使用派生类型定义扩展方法时包括泛型(C# 中的 <T>
,VB 中的 (Of T)
)。您的初始代码没有这样做 - hard-coding 期望基本类型。
以这种方式使用泛型时,您可以添加一个约束,以便 T
必须继承所需的基类型。 (从技术上讲,这适用于任何通用方法,因此扩展方法也以这种方式工作很方便。)
见下文,显示为便于测试的控制台应用程序:
Option Strict On
Imports System.Runtime.CompilerServices
Module Module1
Sub Main()
Dim mock As New Mock(Of [Set](Of Derived))
mock.SetupMock(New List(Of Derived))
'also compiles, uses same extension
Dim mockBase As New Mock(Of [Set](Of Base))
mockBase.SetupMock(New List(Of Base))
End Sub
End Module
Public Module Extensions
<Extension>
Public Sub SetupMock(Of T As Base)(Instance As Mock(Of [Set](Of T)), Entities As List(Of T))
'do stuff with Entities
End Sub
End Module
'simple class definitions to illustrate the example
Public Class Base : End Class
Public Class Derived
Inherits Base
End Class
Public Class [Set](Of T) : End Class
Public Class Mock(Of T) : End Class
如果您的实体列表始终是基本类型的列表,但恰好填充了派生类型,您可以改为使用 Entities As List(Of Base)
定义扩展方法,但您的列表最好总是是 List(Of Base)
而不是 List(Of T)
.
我在访问我创建的扩展方法时遇到问题,该扩展方法采用基数为 class 的对象作为其类型参数。
这是基地 class:
Namespace Models.Db
Public Class Base
Public Property Id As Integer
End Class
End Namespace
...和子class:
Namespace Models.Db
Public Class City
Inherits Base
...
End Class
End Namespace
扩展方法如下:
Imports System.Collections.Generic
Imports System.Data.Entity
Imports System.Linq
Imports System.Runtime.CompilerServices
Imports Matrix.Models
Imports Moq
Public Module Extensions
<Extension>
Public Sub SetupMock(Instance As Mock(Of DbSet(Of Db.Base)), Entities As List(Of Db.Base))
Instance.As(Of IQueryable(Of Db.Base)).Setup(Function(Cities) Cities.GetEnumerator).Returns(Entities.GetEnumerator)
Instance.As(Of IQueryable(Of Db.Base)).Setup(Function(Cities) Cities.ElementType).Returns(Entities.AsQueryable.ElementType)
Instance.As(Of IQueryable(Of Db.Base)).Setup(Function(Cities) Cities.Expression).Returns(Entities.AsQueryable.Expression)
Instance.As(Of IQueryable(Of Db.Base)).Setup(Function(Cities) Cities.Provider).Returns(Entities.AsQueryable.Provider)
End Sub
End Module
...这是调用代码:
Private Function GetDbContextMock() As Mock(Of Db.Context)
Dim oCitiesMock As Mock(Of DbSet(Of Db.City))
oCitiesMock = New Mock(Of DbSet(Of Db.City))
oCitiesMock.SetupMock(Me.Cities) ' Me.Cities is a List(Of Db.City)
End Function
编译时错误为:
'SetupMock' is not a member of 'Mock(Of DbSet(Of City))'
但是,当我将调用代码更改为:
Dim oCitiesMock As Mock(Of DbSet(Of Db.Base))
oCitiesMock = New Mock(Of DbSet(Of Db.Base))
...找到扩展方法。但是我需要向扩展方法发送一个 subclass 引用,而不是一个 base class 引用。
This answer 表示可以使用简单的 superclass/subclass 对象,但由于这是处理类型参数,所以有点复杂。
This answer 乍一看似乎可以提供解决方案,但经过进一步思考,它只解决了一个级别的类型参数。上面的代码有两个。我不确定这种细微差别是否会有所不同,但最终代码无法编译。
这可以在 .NET 中完成吗?
您的第二个链接答案是正确的。额外的 'level' 打字并不重要。该答案的关键点是在使用派生类型定义扩展方法时包括泛型(C# 中的 <T>
,VB 中的 (Of T)
)。您的初始代码没有这样做 - hard-coding 期望基本类型。
以这种方式使用泛型时,您可以添加一个约束,以便 T
必须继承所需的基类型。 (从技术上讲,这适用于任何通用方法,因此扩展方法也以这种方式工作很方便。)
见下文,显示为便于测试的控制台应用程序:
Option Strict On
Imports System.Runtime.CompilerServices
Module Module1
Sub Main()
Dim mock As New Mock(Of [Set](Of Derived))
mock.SetupMock(New List(Of Derived))
'also compiles, uses same extension
Dim mockBase As New Mock(Of [Set](Of Base))
mockBase.SetupMock(New List(Of Base))
End Sub
End Module
Public Module Extensions
<Extension>
Public Sub SetupMock(Of T As Base)(Instance As Mock(Of [Set](Of T)), Entities As List(Of T))
'do stuff with Entities
End Sub
End Module
'simple class definitions to illustrate the example
Public Class Base : End Class
Public Class Derived
Inherits Base
End Class
Public Class [Set](Of T) : End Class
Public Class Mock(Of T) : End Class
如果您的实体列表始终是基本类型的列表,但恰好填充了派生类型,您可以改为使用 Entities As List(Of Base)
定义扩展方法,但您的列表最好总是是 List(Of Base)
而不是 List(Of T)
.