【问题标题】:How can I cast an object to a Func<In, Out> without knowing the generics?如何在不知道泛型的情况下将对象转换为 Func<In, Out>?
【发布时间】:2022-10-24 21:13:31
【问题描述】:

我有很多函数都接受一个类(请求)并返回一个类(响应),我正在尝试编写一个通用处理程序。

我无法更改我的类JobParametersModel 的定义以接受泛型或我的方法DoWorkAsync,但我可以修改其他所有内容。

鉴于这些类:

public class JobRequestModel<Request, Response>
    where Request : class
    where Response : class
{
    public Func<Request, Task<Response>> CallDelegate { get; set; }

    public async Task<Response> DoCall(Request request)
    {
        return await CallDelegate(request);
    }
}

public sealed class JobParametersModel
{
    // Stores class JobRequestModel and other params
    public object RequestModel { get; set; }
}

这是我试图解决的方法。我怎样才能让对象变成我可以与之交互的东西?

// What I'm trying to do, that does not work.
public async Task<JobResultModel> DoWorkAsync(JobParametersModel work, object request)
{
    var jobRequestModel = (JobRequestModel)work.RequestModel; // Does NOT work.
    
    // I can cast request using "ChangeType". Simplified code for example
    var results = await jobRequestModel.DoCall(request);
    
    // I package/return results later
}

这样我就可以这样称呼它:

var result1 = await DoWorkAsync(
    new JobParametersModel()
    {
        RequestModel = new JobAXRequestModel<CustomRequestType, CustomResponseType>()
        {
            CallDelegate = _client.getMyCustomDelegate
        }
    },
    new CustomRequestType()
    {
        CustomField1 = "something",
        CustomField2 = 4
    }
);

var result2 = await DoWorkAsync(
    new JobParametersModel()
    {
        RequestModel = new JobAXRequestModel<OtherCustomRequestType, OtherCustomResponseType>()
        {
            CallDelegate = _client.getSomeOtherCustomDelegate
        }
    },
    new OtherCustomRequestType()
    {
        DifferentField1 = "other things"
    }
);

【问题讨论】:

  • 既然你必须写RequestModel = new JobAXRequestModel&lt;CustomRequestType, CustomResponseType&gt;(),你能不能简单地写public async Task&lt;JobResultModel&gt; DoWorkAsync&lt;Request, Response&gt;(JobParametersModel work, object request) where Request : class where Response : classvar jobRequestModel = (JobRequestModel&lt;Request, Response&gt;)work.RequestModel;
  • 代码被简化了,但它实际上是更大的队列/轮询框架的一部分。我不直接调用该方法,因此很遗憾,如果不重写整个框架,我就无法修改该函数定义。
  • 这与我无法更改我的类 JobParametersModel 的定义以接受泛型,但我可以修改其他所有内容:)
  • 也许您可以扔几只鸭子并将var jobRequestModel = (JobRequestModel)work.RequestModel 替换为dynamic jobRequestModel = work.RequestModel - 一旦它变成动态的,那么只要签名匹配,您就应该能够调用DoCall 方法。
  • @MetroSmurf 看起来我能够让它工作!这是访问泛型的最佳方式还是应该添加更多反射检查?

标签: c# asp.net generics async-await casting


【解决方案1】:

在必须使用预定义签名实现方法但您需要访问其他类型的情况下,则存在方法内的鸭子类型选项。

参考见:Using type dynamic (C# Programming Guide)

在您的示例代码中,必须使用以下签名实现以下方法:

Task<JobResultModel> DoWorkAsync(JobParametersModel work, object request)

其中JobParametersModel 类型包含带有模型的object

public object RequestModel { get; set; }

由于您无法更改DoWorkAsync 方法的签名,也无法更改JobParametersModel 的结构,因此您可以利用动态编程并通过运行时验证绕过编译时检查:

dynamic jobRequestModel = work.RequestModel

最终实施将在哪里:

public async Task<JobResultModel> DoWorkAsync(JobParametersModel work, object request)
{
    // cast the the model to a dynamic type
    // could also be defined as: 
    // dynamic jobRequestModel = work.RequestModel
    var jobRequestModel = (dynamic)work.RequestModel;
    
    // invoke the method; type checking will only during runtime
    var results = await jobRequestModel.DoCall(request);
    
    // other code...
}

只要该方法存在于dynamic 类型上,那么运行时就会执行该方法。但是,如果该方法不存在或具有不同的签名,则将引发运行时异常。

如果您希望将JobRequestModel 以外的类型作为RequestModel 传入,那么您将需要在调用方法之前执行额外的检查以验证类型(可以像反射一样简单以找到匹配的方法)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-11
    • 1970-01-01
    • 2016-08-08
    • 1970-01-01
    • 2013-03-05
    • 2011-02-10
    • 1970-01-01
    相关资源
    最近更新 更多