Return 泛型方法中的特定类型取决于运行时没有反射或动态的枚举值

Return specific type in generic method depending on enum value without Reflection or dynamic during runtime

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

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

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

图书馆代码中有以下 class:

public class ListView //THIS IS LIBRARY CODE AND CAN NOT BE CHANGED
{
  public IList itemsSource { get; set; }
}

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

另一个限制是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");
        }
    }
}

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

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

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");
    }
}

我将如何实施?

Edit/Appendage

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

Class A 和 B 确实有相同的碱基 class。 但我希望能够不再从基本类型转换它(因为我已经在 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。它应该以某种方式隐式 return 正确的项目。

为了您的信息,我使用 Unity UI 工具包框架,但这与问题并不相关。

想想这个方法怎么用:

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

itemType的值可能是A,也可能是B。那么 x 的类型应该是什么?它要么是 IList<A>,要么是 IList<B>,唯一的表达方式是使用 IList<A>IList<B> 继承的类型,这让我们回到 IList。如果您在编译时不知道您想要的类型,那么尝试将 return 类型更改为更具体的类型没有多大意义,因为您将无法访问它作为更具体的类型没有反映。

如果您知道您在编译时想要的类型,那么提供return所需类型的方法的重载:

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

编辑

如果您真的想要使用通用方法,您可以这样做:

public IList<T> GetItems<T>()
{
    return itemsSource.Cast<T>().ToList();
}

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

编辑 2

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

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
    }
}

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

你可以试试这些:

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 在评论中所建议的那样:

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");
    }
}