如何创建与嵌套类型参数的子类一起使用的扩展方法?

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).