在泛型方法中返回特定类型,具体取决于运行时没有反射或动态的枚举值

Posted

技术标签:

【中文标题】在泛型方法中返回特定类型,具体取决于运行时没有反射或动态的枚举值【英文标题】:Return specific type in generic method depending on enum value without Reflection or dynamic during runtime 【发布时间】:2021-12-28 17:17:18 【问题描述】:

我有一个非泛型 IList,我想在运行时根据 enum 值进行转换。

我无法在实现中更改非泛型 IList 的类型,因为它是库代码。

我也不想使用反射(因为它很慢)或动态关键字(因为它不安全并且可能导致错误)。

在图书馆代码中有以下类:

public class ListView //THIS IS LIBRARY CODE AND CAN NOT BE CHANGED

  public IList itemsSource  get; set; 

然后在继承自 ListView 的 CustomListView 类中,我想根据 ItemType 将 itemsSource 转换为适当的类。

另一个限制是 CustomListView 不能是通用的(由我们使用的库决定)

public class CustomListView : ListView

    public dynamic GetItems()
    
        switch (ItemType)
        
            case ItemType.A:
              return itemsSource.Cast<A>().ToList();
            case ItemType.B:
              return itemsSource.Cast<B>().ToList();
            default:
              throw new InvalidOperationException("Not supported");
        
    

但我希望它直接返回正确的类型,而不是使用动态。

类似的东西(下面的代码不起作用!):

public IList<T> GetItems(ItemType itemType) //The type of T should change depending on what i return

    switch (ItemType)
    
        case ItemType.A:
          return itemsSource.Cast<A>().ToList();
        case ItemType.B:
          return itemsSource.Cast<B>().ToList();
        default:
          throw new InvalidOperationException("Not supported");
    

我将如何实现它?

编辑/附加

正如你们指出的,我应该澄清一些事情。

A 类和 B 类确实具有相同的基类。 但是我希望能够不再从基本类型中转换它(因为我已经在 GetItems() 方法中转换它并且 ItemType 的值也是已知的)。

我希望能够做到以下几点

IList<A> aItems = listView.GetItems()

没有强制转换。

所有这一切背后的想法是拥有一个可以处理多种项目类型的通用 CustomListView。这些项目将被添加到 itemSource。这些项目的类型由 ItemType 确定。

我就是这样用的

public class UiForClassA

  public void Foo()
  
    CustomListView customListView = new CustomListView(ItemType.A);

    IList<A> itemsOfCustomListView = customListView.GetItems(); //No cast needed because it should somehow implicitly know that.
  

我不想在使用 CustomListView 的任何地方都使用 Casts。它应该以某种方式隐式返回正确的项目。

为了您的信息,我使用 Unity UI Toolkit Framework,但这与问题并不真正相关。

【问题讨论】:

如果类“A”和“B”没有通用功能,那么您应该返回“IList”。但是您可能应该有某种抽象,例如通用接口。然后你就可以返回 'IList'。 你不能这样做。指定类型参数的是调用者。您必须声明 public IList&lt;T&gt; GetItems&lt;T&gt;() 并使用 var list = GetItems&lt;A&gt;(); 调用它。如果GetItems 返回不同类型的列表,你想如何消费它们?消费者也需要是通用的。 【参考方案1】:

想一想如何使用这种方法:

ItemType itemType = ...;
var x = listView.GetItems(itemType);

itemType 的值可以是A,也可以是B。那么x 的类型应该是什么?它要么是IList&lt;A&gt;,要么是IList&lt;B&gt;,唯一的表达方式是使用IList&lt;A&gt;IList&lt;B&gt; 都继承自的类型,这会将我们带回到IList。如果您在编译时不知道您想要的类型,那么尝试将返回类型更改为更具体的类型没有多大意义,因为您将无法在没有反射的情况下将其作为更具体的类型访问。

如果您确实在编译时知道所需的类型,则提供返回所需类型的方法的重载:

public IList<A> GetAItems()  ... 
public IList<B> GetBItems()  ... 

编辑

如果你真的想要使用泛型方法,你可以这样做:

public IList<T> GetItems<T>()

    return itemsSource.Cast<T>().ToList();

但是我看不出为此创建方法的意义,而不是将代码直接放在需要特定类型的地方。

编辑 2

看过您的用例后,我真的建议您将 CustomListView 类设为通用。我知道您说图书馆会阻止这种情况,但是有一些方法可以解决这个问题。你已经创建了一个ItemType 枚举,你可以为每个项目类型创建一个CustomListView&lt;T&gt; 的子类:

public class CustomListView<T> : ListView

    public IList<T> GetItems() => itemsSources.Cast<T>().ToList();


public class CustomListViewOfA : CustomListView<A>  
public class CustomListViewOfB : CustomListView<B>  

public class UiForClassA

    public void Foo()
    
        var customListView = new CustomListViewOfA();
        var itemsOfCustomListView = customListView.GetItems(); //No cast needed
    

【讨论】:

只是我明确设置了一个 ItemType 但仍然必须在特定代码中手动转换它。所以我想要一个根据 ItemType 隐式返回正确类型的方法。我不想在此 ListView 的每次使用中都添加 Cast 语句,我想要一种通用方法来处理它。 我还是不明白你为什么需要一个ItemType 变量。如果您向我们展示您要如何处理这些物品,将会有所帮助。看起来您正在使用 UI 库/框架 - 如果您提供有关包含 ListView 的类的一些详细信息,并尝试访问特定类型的项目,人们可能能够提供更好的建议【参考方案2】:

您希望如何使用这种方法?类型参数由方法的调用者设置。无法从方法中设置类型参数。请说明用途。

你可以试试这些:

IList<T> GetItems<T>(ItemType itemType)

    switch (itemType)
    
        case ItemType.A:
          return (IList<T>)(IList)(itemsSource.Cast<A>().ToList());
        case ItemType.B:
          return (IList<T>)(IList)(itemsSource.Cast<B>().ToList());
        default:
          throw new InvalidOperationException("Not supported");
    

IList GetItems(ItemType itemType)

    switch (itemType)
    
        case ItemType.A:
          return itemsSource.Cast<A>().ToList();
        case ItemType.B:
          return itemsSource.Cast<B>().ToList();
        default:
          throw new InvalidOperationException("Not supported");
    

或者,正如 @Qwertyluk 在 cmets 中所建议的那样:

private IList<object> GetItems(ItemType itemType)

    
    switch (itemType)
    
        case ItemType.A:
        case ItemType.B:
          return itemsSource.Cast<object>().ToList();
        default:
          throw new InvalidOperationException("Not supported");
    

【讨论】:

以上是关于在泛型方法中返回特定类型,具体取决于运行时没有反射或动态的枚举值的主要内容,如果未能解决你的问题,请参考以下文章

通过在运行时在泛型方法中进行类型转换来使用递归函数

在泛型方法中返回原始集合类型

泛型类中的数组

JAVA泛型知识点

对泛型编程中泛型类型的一些理解

在泛型类中实现特定方法