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