其他分享
首页 > 其他分享> > 可以传递没有类型参数的泛型委托吗?

可以传递没有类型参数的泛型委托吗?

作者:互联网

我有三个项目

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

MVC参考>服务

服务参考>英孚

我目前有这三种方法可以完成一些工作.

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

我使用以下接口从控制器调用这三种方法,这些接口委托给上述工作方法:

Boolean StoreUpload(UploadInformation information);
Boolean RemoveUpload(UploadInformation information);
Boolean CommitStoredDocuments(UploadInformation information);

根据开关中UploadTypes枚举的条件,我调用正确的工作方法.之所以这样做,是因为我不希望我的mvc项目能够访问EF数据库类型,否则我知道有人将开始从整个应用程序中查询数据.我将这些switch语句用于所有接口方法:

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) { ... }

该方法可能会稍微说明类型参数的用途.我正在使用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);
 ... 

} 

能够传递需要类型参数的工作方法作为开关工作方法调用的委托,这将是很好的.

public bool DoSomeWork(delegateMethod, information) {
    switch(information.Type) { 
         case UploadTypes.AutoInciden:
           return delegateMethod<AutoIncident>(information);
         ...
    }
} 

能做到吗?
另外,我在为这个问题构建一个好标题时遇到了麻烦,因此请评论一下这些是否是描述挑战的更好方法.

解决方法:

由于多种原因,它不能直接完成.

首先,您可能已经注意到,委托方法< FooBar>(信息)根本无法编译.这是因为在您的示例中,委托方法是局部变量(实际上是方法参数,但仍然是变量),并且您不能应用“类型参数”< FooBar>.变量-您只能将它们应用于表示(通用)类型或(通用)方法的标识符.

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

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

您实际上需要指定Action的类型,以便从称为Blah的一般描述中选择适当版本的泛型方法.这些委托绑定到方法的具体版本,并且仅接受该类型.这些委托根据其类型参数被“关闭”.使用正常方式,MagicDoSomeWork将根本无法更改这些代表已经记住的T.

这两件事是一种显示停止器,因为仅通过常规代码,您就无法编写类似

var nope1 = new Action(Blah);  // ctor for Action NEEDS type parameter

因为Action构造函数只需要类型参数.一旦您传递了任何内容,它将锁定Blah类型的参数

另外,您不能使用开放委托:

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

因为new运算符需要完整类型才能创建对象.

但是,通过一点反射伏都教,就可以动态地分析和构建泛型或泛型方法.

// first, build the delegate in a normal way
// and pick anything as the type parameters
// we will later replace them
var delegateWithNoType = new Action<object>(Blah);
// delegate has captured the methodinfo,
// but uses a stub type parameter - it's useless to call it
// but it REMEMBERS the method!

// .... pass the delegate around

// later, elsewhere, determine the type you want to use
Type myRealArgument;
switch(..oversomething..)
{
    default: throw new NotImplemented("Ooops");

    case ...: myRealArgument = typeof(UploadTypes.AutoIncident); break;
    ...
}

// look at the delegate definition
var minfo = delegateWithNoType.Method;
var target = delegateWithNoType.Target; // probably NULL since you cross layers

var gdef = minfo.GetGenericDefinition();
var newinfo = gdef.MakeGenericMethod( myRealArgument );

// now you have a new MethodInfo object that is bound to Blah method
// using the 'real argument' type as first generic parameter
// By using the new methodinfo and original target, you could now build
// an updated delegate object and use it instead the original "untyped" one
// That would be a NEW delegate object. You can't modify the original one.

// ...but since you want to call the method, why don't use the methodinfo

UploadInformation upinfo = ... ;
newinfo.Invoke(target, new object[] { upinfo });
// -> will call Blah<UploadTypes.AutoInciden>(upinfo)

警告:这是向您展示委托的草图.Method / Target和methodinfo以及getgenericdefinition和makegeneric方法.我是从内存中写出来的,从未编译过,从未运行过.它可能包含轻微的错别字,被忽视的事物和看不见的彩虹独角兽.我什么都没注意到.可能是因为它们不可见.

标签:generics,delegates,c,asp-net-mvc,entity-framework
来源: https://codeday.me/bug/20191118/2026432.html