【问题标题】:How to store a generic function in a variable in c#? [duplicate]如何将泛型函数存储在c#中的变量中? [复制]
【发布时间】:2019-12-10 11:38:52
【问题描述】:

我有以下代码:

public interface IMenuItem
{
    IWebElement Parent { get; }
}


public class MenuItems
{
    public T GetMenuItem<T>() where T : IMenuItem, new()
    {
        var menuItem = new T();

        return menuItem;
    }
}


public abstract class Page : IEnumerable<IComponent>
{
    private Func<IMenuItem> _getMenuItems = new MenuItems().GetMenuItem<IMenuItem>;
}

我正在尝试将 new MenuItems().GetMenuItem&lt;IMenuItem&gt; 函数存储在 _getMenuItems 字段中,但由于这是一个通用函数,因此无法正常工作。如何将泛型函数存储到变量中?

执行new MenuItems().GetMenuItem&lt;IMenuItem&gt; 不起作用,它告诉我:

“IMenuItem”必须是具有公共无参数构造函数的非抽象类型,才能将其用作泛型类型或方法“MenuItems.GetMenuItem()”中的参数“T”

现在我有许多实现IMenuItem 的具体类型,我希望代表接受这些。我不想为每个实现IMenuItem 的具体类型创建一个单独的委托。相反,我有一个可以接受实现 IMenuItem 的具体类型的委托。

【问题讨论】:

  • 您是否真的打算在每次调用委托时创建(并丢弃)一个新的 MenuItems 实例?这似乎是设计的一个潜在问题。 GetMenuItem 应该是静态的,或者 MenuItems 实例本身应该在其他地方创建。
  • 这行得通——除非,正如其他人所指出的,您确实需要将具体类型传递给GetMenuItemIMenuItem 自己不会这样做。您可能希望将其留给MenuItems 来确定类型(例如,将其设为MenuItems&lt;T&gt;),或者您希望将Func&lt;IMenuItem&gt; 传递给it,而不是有一个new() 约束,所以由调用者决定实例化什么以及如何实例化。
  • 其实这是一个很好的问题,所以我是否正确理解您想要使用这个变量_getMenuItems 像这样:IMenuItem temp = _getMenuItems&lt;MyTypeImplementingIMenuItem&gt;()?你希望它作为一个变量而不是一个方法?=!因为一个方法只能是 new MenuItems().GetMenuItem&lt;IMenuItem&gt;() 调用的包装器?=!
  • @MongZhu 没错!我不需要方法包装器我想像你说的那样在变量中执行此操作:IMenuItem temp = _getMenuItems&lt;MyTypeImplementingIMenuItem&gt;()
  • 我想你是想写private Func&lt;IMenuItem&gt; _getMenuItems = () =&gt; menuItems.GetMenuItem&lt;IMenuItem&gt;();

标签: c# .net generics asp.net-core .net-core


【解决方案1】:

问题在于new() 约束。

新约束指定泛型类声明中的类型参数必须具有公共无参数构造函数。要使用新的约束,类型不能是抽象的。

由于IMenuItem 是一个接口,它不能被实例化,因此new() 约束将是一个问题。

编辑:

您可以通过以下几种方式解决此问题:

  1. 您可以使用反射来检索方法并调用它:

    var method = typeof(MenuItems).GetMethod("GetMenuItem");
    var instance = new MenuItems(); // Maybe convert the class into a static class??
    var genericMethod = method.MakeGenericMethod(typeof(MenuItem1));
    var result = genericMethod.Invoke(instance, null) as IMenuItem;
    
  2. 使用动态调用它:

    dynamic instance = new MenuItems(); // Maybe convert the class into a static class??
    var result = instance.GetMenuItem<MenuItem1>() as IMenuItem;
    
  3. 持有一个 MenuItems 的实例而不是方法本身:

    var menuItems = new MenuItems();
    var result = _instance.GetMenuItem<MenuItem1>() as IMenuItem;
    

【讨论】:

    【解决方案2】:

    我认为这里的字段不合适。你能做的最好的就是一个包装方法:

    public abstract class Page : IEnumerable<IComponent>
    {
        private static readonly MenuItems menuItems = new MenuItems();        
    
        protected Func<T> GetMenuItems<T>() where T : IMenuItem, new()
        {
            return () => menuItems.GetMenuItem<T>();
        }
    
    }
    

    【讨论】:

      【解决方案3】:

      在您的MenuItems 类中,new 表示

      这是对泛型参数 T 的约束。它必须是类(引用类型)并且必须具有公共无参数默认构造函数。

      这意味着 T 不能是 int、float、double、DateTime 或任何其他结构(值类型)。

      来源:https://stackoverflow.com/a/4737985/1349365

      所以你可以用你当前的结构做的是:

      public interface IMenuItem
      {
          IWebElement Parent { get; }
      }
      
      public class MenuItem : IMenuItem
      {
          public IWebElement Parent => throw new NotImplementedException();
      }
      
      public class MenuItems
      {
          public T GetMenuItem<T>() where T : IMenuItem, new()
          {
              var menuItem = new T();
      
              return menuItem;
          }
      }
      public abstract class Page : IEnumerable<IComponent>
      {
          private Func<IMenuItem> _getMenuItems = new MenuItems().GetMenuItem<MenuItem>;
      }
      

      正如你的错误所说,你需要一个具体的类来传递给GetMenuItem 方法。

      【讨论】:

      • 我有许多具体的类实现IMenuItem 与您提供的解决方案我需要为每个具体类创建一个委托。
      • 那么@amir-popovich 建议var menuItems = new MenuItems(); var result = _instance.GetMenuItem&lt;MenuItem1&gt;() as IMenuItem; 是最佳选择。
      猜你喜欢
      • 2016-05-27
      • 2019-11-24
      • 2015-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-29
      • 1970-01-01
      • 2023-03-08
      相关资源
      最近更新 更多