C# 是否可以在不使用类型参数的情况下传递泛型委托?

C# 是否可以在不使用类型参数的情况下传递泛型委托?,c#,asp.net-mvc,entity-framework,generics,delegates,C#,Asp.net Mvc,Entity Framework,Generics,Delegates,我有三个项目 MVC Web应用程序 服务应用程序是一种两层业务/存储库 实体框架(所有EF配置都位于此处) MVC参考资料>服务 服务参考资料>EF 我目前有这三种方法可以做一些工作 public bool StoreUpload<T>(UploadInformation information) where T : class, IUploadEntity { } public bool RemoveUpload<T>(UploadInformation i

我有三个项目

  • MVC Web应用程序
  • 服务应用程序是一种两层业务/存储库
  • 实体框架(所有EF配置都位于此处)
  • MVC参考资料>服务

    服务参考资料>EF

    我目前有这三种方法可以做一些工作

    public bool StoreUpload<T>(UploadInformation information) 
       where T : class, IUploadEntity { }
    
    public bool RemoveUpload<T>(UploadInformation information) 
       where T : class, IUploadEntity { }
    
    public bool CommitUpload<T>(UploadInformation information) 
       where T : class, IUploadEntity { }
    
    基于开关中UploadTypes枚举的条件,我调用了正确的工作方法。我这样做是因为我不希望我的mvc项目能够访问EF数据库类型,否则我知道有人将开始从整个应用程序中查询数据。我将这些开关语句用于所有接口方法:

    public bool StoreUpload(UploadInformation information)
    {            
        switch (information.Type)
        {
            case UploadTypes.AutoIncident:
                return RemoveUpload<AutoIncident>(information);
            case UploadTypes.Incident:
                return RemoveUpload<IncidentInjury>(information);
            case UploadTypes.Inspection:
                return RemoveUpload<Inspection>(information);
            case UploadTypes.OtherIncident:
                return RemoveUpload<OtherIncident>(information);
            default:
                return false;
        }
    }
    
    public bool RemoveUpload(UploadInformation information) { ... }
    public bool CommitStoredUpload(UploadInformation information) { ... }
    
    public bool StoreUpload(上传信息)
    {            
    开关(信息类型)
    {
    案例上载类型。自动事件:
    返回RemoveUpload(信息);
    案例上载类型。事件:
    返回RemoveUpload(信息);
    案例上传类型。检查:
    返回RemoveUpload(信息);
    案例上载类型。其他事件:
    返回RemoveUpload(信息);
    违约:
    返回false;
    }
    }
    公共bool RemoveUpload(上传信息){…}
    公共bool committestoredupload(上传信息){…}
    
    此方法可能有助于了解类型参数的用途。我正在使用EF以通用方式更新表

    private bool CommitStoredDocuments<T>(UploadInformation information) where T : class, IUploadEntity
    {
            var uploads = GetStoredUploads(information.UniqueId);
            var entity = db.Set<T>().Include(e => e.Uploads)
                 .Single(e => e.UniqueId == information.UniqueId);
            entity.Uploads.AddRange(uploads);
     ... 
    
    } 
    
    private bool CommitStoredDocuments(上传信息),其中T:class,IUploadEntity
    {
    var uploads=GetStoredUploads(information.UniqueId);
    var entity=db.Set().Include(e=>e.Uploads)
    .Single(e=>e.UniqueId==information.UniqueId);
    entity.Uploads.AddRange(上传);
    ... 
    } 
    
    如果能够将需要类型参数的work方法作为委托传递给switch work方法调用,那就太好了

    public bool DoSomeWork(delegateMethod, information) {
        switch(information.Type) { 
             case UploadTypes.AutoInciden:
               return delegateMethod<AutoIncident>(information);
             ...
        }
    } 
    
    public bool DoSomeWork(委托方法、信息){
    开关(信息类型){
    案例上载类型.AutoInciden:
    返回委托方式(信息);
    ...
    }
    } 
    
    这能做到吗?
    此外,我在为这个问题构建一个好的标题时遇到了困难,因此,如果这是一个更好的描述挑战的方法,请发表评论。

    由于几个原因,无法直接完成

    首先,正如您可能注意到的,
    delegateMethod(information)
    根本无法编译。这是因为在您的示例中,
    delegateMethod
    是一个局部变量(实际上是一个方法参数,但仍然是一个变量),您不能将“类型参数”
    应用于变量-您只能将其应用于指示(泛型)类型或(泛型)方法的标识符

    第二个原因更有趣。当您将方法作为委托传递时,委托实际上捕获整个方法签名,包括所有参数类型

    void Blah<T>(UploadInformation information){ ... }
    
    var one = new Action<int>(Blah); // -> Blah<int>
    var two = new Action<float>(Blah); // -> Blah<float>
    var thr = new Action<andsoon>(Blah); // -> Blah<andsoon>
    
    MagicDoSomeWork(one, ...); // these all
    MagicDoSomeWork(two, ...); // delegates are already bound
    MagicDoSomeWork(thr, ...); // and remember their concrete T
    
    因为动作构造函数只需要一个类型参数。一旦您传递了any,它将锁定Blah类型的参数

    此外,您不能使用打开的委托:

    var nope1 = new Action<>(Blah); // can't use empty <> in this context :(
    

    警告词:这是一个草图,向您展示了
    委托方法/目标
    方法信息
    getgenericdefinition
    makegenericmethod
    的工作原理。我是凭记忆写的,从未编译过,也从未运行过。它可以包含一些小的拼写错误、被忽略的东西和看不见的彩虹独角兽。我没有注意到。可能是因为它们是隐形的。

    你可以这样做

    public bool Invoke(EntityType entityType, ActionType action, Object[] arguments)
        {
            var actionType = Enum.GetName(typeof(ActionType), action);
            var type = GetType();
            var method = type.GetMethods().Single(m => m.IsGenericMethod && m.Name == actionType);
    
            switch (entityType)
            {
                case EntityType.IncidentInjury:
                    var genericMethod = method.MakeGenericMethod(typeof(IncidentInjury));
                    return (bool)genericMethod.Invoke(this, arguments);
                default:
                    return false;
            }
        }
    

    枚举将只是我希望以这种方式调用的方法的列表,我为我的服务创建了一个基类,这样我就不必将实例传递给invoke方法

    >不使用委托,而是考虑使用接口(或抽象类)。这样,您的方法可以保留其泛型性质

    例如,如果您创建一个界面,如:

    interface IUploadAction
    {
        bool Perform<T>(UploadInformation information)
           where T : class, IUploadEntity;
    }
    
    您的切换/调度方法如下所示:

    public bool DoAction(IUploadAction action, UploadInformation information)
    {
        switch (information.Type)
        {
            case UploadTypes.AutoIncident:
                return action.Perform<AutoIncident>(information);
            case UploadTypes.Incident:
                return action.Perform<IncidentInjury>(information);
            case UploadTypes.Inspection:
                return action.Perform<Inspection>(information);
            case UploadTypes.OtherIncident:
                return action.Perform<OtherIncident>(information);
            default:
                return false;
        }
    }
    

    如果
    DoSomeWork
    方法已经包含对正确方法的引用(使用
    delegateMethod
    ),为什么还要再次使用
    switch
    子句?我是从一个无法访问这些类型的层调用的。谢谢,这就是我的想法。它将制定出代码。这似乎是解决这一挑战的办法。我想到了这个解决方案,但我想先知道。@jwize:我稍微编辑了一下文本,以便在一些地方使用您的字体名称,这样应该更容易阅读。顺便说一句,你选择的标题很好。在这种情况下,传递代理是很棘手的。我忘了写的是,您实际上可能不需要委托,特别是如果您的目标是
    null
    targets。对于未来的管理者来说,传递“裸”方法信息可能更容易理解。(续)@jwize:您仍然可以使用Action/Func等委托来捕获方法,然后当您将它们传递到另一层时,您可以选择它们的.Method,然后获取.GetGenericDefinition并将其传递到另一层。您将传递开放泛型方法声明,但尚未填充任何类型参数。对于未来的读者来说,这将是一个非常明确的“暂时不要使用我”的标志。但是,它看起来比仅仅将动作传递给下面的层要复杂得多。功能上是相同的(除非.Target是!=null),所以选择您更喜欢的。抱歉,我不确定我是否遵循此选项?然后,我必须为每个实现创建一个新的开关,这正是我试图避免的。您的代码也需要仔细查看。@jwize我误解了问题所在,我将更新我的答案。。。那有帮助吗?是的,我就是这样做的。
    public bool Invoke(EntityType entityType, ActionType action, Object[] arguments)
        {
            var actionType = Enum.GetName(typeof(ActionType), action);
            var type = GetType();
            var method = type.GetMethods().Single(m => m.IsGenericMethod && m.Name == actionType);
    
            switch (entityType)
            {
                case EntityType.IncidentInjury:
                    var genericMethod = method.MakeGenericMethod(typeof(IncidentInjury));
                    return (bool)genericMethod.Invoke(this, arguments);
                default:
                    return false;
            }
        }
    
    interface IUploadAction
    {
        bool Perform<T>(UploadInformation information)
           where T : class, IUploadEntity;
    }
    
    class CommitStoredDocuments : IUploadAction
    {
        public bool Perform<T>(UploadInformation information)
           where T : class, IUploadEntity
        {
            var uploads = GetStoredUploads(information.UniqueId);
            var entity = db.Set<T>().Include(e => e.Uploads)
                 .Single(e => e.UniqueId == information.UniqueId);
            entity.Uploads.AddRange(uploads);
    
            //...
        }
    }
    
    public bool DoAction(IUploadAction action, UploadInformation information)
    {
        switch (information.Type)
        {
            case UploadTypes.AutoIncident:
                return action.Perform<AutoIncident>(information);
            case UploadTypes.Incident:
                return action.Perform<IncidentInjury>(information);
            case UploadTypes.Inspection:
                return action.Perform<Inspection>(information);
            case UploadTypes.OtherIncident:
                return action.Perform<OtherIncident>(information);
            default:
                return false;
        }
    }
    
    IUploadAction storeUpload;
    
    public bool StoreUpload(UploadInformation information) => DoAction(storeUpload, information);