【问题标题】:Can the 'T' part in a generic List(Of T) somehow be a variable?通用 List(Of T) 中的“T”部分能否以某种方式成为变量?
【发布时间】:2019-09-05 19:44:41
【问题描述】:

我有一个没有指定返回类型的函数,它根据枚举 type 输入参数返回一个通用 List(Of T),因此:

Public Function GetTypeList(type as TypeListEnum)
    Select Case type
        Case TypeListEnum.A
            'create result As List(Of A)
            Return result
        Case TypeListEnum.B
            'create result As List(Of B)
            Return result
        ...etc.
        Case Else
            Return Nothing
    End Select
End Function

在每个块中创建结果的代码行都是相同的——除了定义要返回的列表类型的部分——所以我想如果我能以某种方式指定变量的 T 是什么会很好,而不是在多个案例块中拥有那些几乎相同的代码块。

有什么办法吗?

如果没有,是否有另一种(也许更好)方法可以根据输入参数返回不同的通用列表?

【问题讨论】:

  • 这个函数本身应该是通用的可能性很大。但它is possible.
  • 这可以使用泛型来完成。
  • type 参数的值究竟来自哪里,即您调用此方法的准确程度如何?我同意@HansPassant 的观点,最可能的解决方案是重新实现您拥有的通用方法。
  • @jmcilhinney,你是什么意思,type 是从哪里来的?” 这是一个基本的公共枚举,所以该函数被调用例如Dim myList as List(Of Booking) = GetTypeList(TypeListEnum.Booking).
  • 那么你的意思是你只是在硬编码它?如果是这样,那么您绝对应该使用通用方法并改用Dim myList = GetTypeList(Of Booking)()。顺便说一句,你显然有Option Strict Off,这很糟糕。这是您当前拥有的代码可以编译的唯一方法,因此它显然是错误的代码。

标签: vb.net list generics


【解决方案1】:

您可以提供您期望的类型作为函数的通用约束

Public Function GetList(Of T)() AS List(Of T)
    Select GetType(T) 
        Case GetType(First)
            Return New List(Of First) From { New First() }.Cast(Of T)()
        Case GetType(Second)
            Return New List(Of Second) From { New Second() }.Cast(Of T)()
        Case Else
            Return New List(Of T)()
    End Select
End Function

使用该方法会比使用专用枚举更具可读性。

Dim myList As List(Of First) = GetList(Of First)()
Dim myList As List(Of Second) = GetList(Of Second)()

请注意,您需要将结果列表显式转换为通用列表 List(Of T)
在您的特定情况下,您可能会有一些可用于泛型类型的通用约束。

另外,除非您想为未配置的类型抛出异常,否则我建议您善待您的方法的使用者并返回一个空列表而不是 Nothing

【讨论】:

  • 谢谢法比奥。我刚刚尝试过,虽然它确实有效,但基准测试表明它在多次迭代中比我现有的枚举评估方法慢得多。性能是这个功能的一个关键因素,所以很遗憾我不能使用它。但这仍然是我的问题(没有提到性能)的一个很好的答案,如果性能对他们来说不是问题,它有望向其他人展示如何做到这一点。
  • 这取决于您创建列表的方式。您可以在一次迭代中转换所有对象,然后将其转换为 List
  • 我不明白为什么使用泛型方法会显着影响性能。我猜你做错了什么,但如果没有看到相关代码,我们无法知道你是不是或者它可能是什么。
  • 这里整个SwitchGetType()是不必要的。另外,你为什么用一项来填充列表?原始代码中没有。
  • @Vilx-,开关基于 OP 的示例,据我了解,不同类型有不同的逻辑来创建列表。
【解决方案2】:
  1. 您需要有一个Type 的实例才能知道泛型的类型

  2. 你可以通过名字GetType,调用GetType并传递类型的名字

  3. 您可以通过调用.ToString() 将枚举转换为String

  4. 你有一个枚举值作为输入

总结一下:您需要通过调用.ToString() 将枚举输入转换为String,并将其用作GetType 的参数,并使用GetType 返回的Type 来实例化您的集合。

【讨论】:

    【解决方案3】:

    嗯,枚举值数据类型实际上没有任何共同之处。从一个到另一个没有“内置”的方式。所以无论如何你都需要某种映射,比如“对于这个枚举值,采用这种类型”。

    一种方法是你的 switch 语句。我其实很喜欢它,我认为它也会有很好的表现。可读性也很好——你可以立即看到哪个枚举值映射到哪个类型。

    或者,您可以制作某种Dictionary,将枚举值映射到Types。你不会有一个开关,但你需要使用反射来创建一个实例。这样会慢一些。

    添加:

    如果您可以按照Fabio's answer 更改函数签名,那么您真的可以改进:

    Public Function GetList(Of T)() AS List(Of T)
        Return New List(Of T)()
    End Function
    

    这将更快更容易阅读。但此时你必须要问——把它放在一个单独的函数中有什么意义?为什么不直接拨打New List(Of T)(),而是在需要的地方拨打电话?

    【讨论】:

    • 感谢 Vilx-。在尝试了 Fabio 的建议并发现它影响了性能而对可读性 (IMO) 没有任何影响之后,我倾向于同意我已经拥有的性能比任何反射的使用都要好,所以我想我会坚持下去。
    • @Antagony - Fabio 使用了不必要的反射。没有它,他的解决方案可以变得更漂亮。我没有质疑枚举的使用,但是如果您可以将方法签名从Public Function GetTypeList(type as TypeListEnum) 更改为Public Function GetList(Of T)() AS List(Of T),那么整个事情确实可以变得更好!
    • @Antagony,GetType 没有使用反射,GetType 将在编译类型期间获取类型,因此由于GetType 的使用,在运行时不可能产生一些性能问题。跨度>
    • @Fabio - GetType(T) 是运行时...并且其他人也发出一些获取类型的指令...但是好的,我同意,这不太可能成为问题。我开始怀疑 Antagony 是如何进行比较的。
    • @Antagony,数据库查询是性能瓶颈。当您尝试执行相同的方法但没有数据库查询时,您应该不会看到很大的性能差异。
    猜你喜欢
    • 2012-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-27
    • 1970-01-01
    • 1970-01-01
    • 2010-09-19
    • 1970-01-01
    相关资源
    最近更新 更多