是否可以创建具有动态签名的抽象方法?

Posted

技术标签:

【中文标题】是否可以创建具有动态签名的抽象方法?【英文标题】:Is it possible to create an abstract method with a dynamic signature? 【发布时间】:2021-10-27 22:47:41 【问题描述】:

我有一个 .Net 5 Web Api 项目,其中多个控制器的行为类似于操作,应该始终返回状态码 200,Guid 代表因果关系 id。

我为这些命令处理程序创建了一个基类

[ApiController]
public abstract class CommandController : ControllerBase

    [NonAction]
    protected CausationIdResult CausationId(Guid causationId) => new(causationId);
    
    public abstract Task<CausationIdResult> Handle(CancellationToken cancellationToken);


public sealed class CausationIdResult : OkObjectResult

    public CausationIdResult(Guid causationId) : base(causationId)  

一个示例实现可以是

public sealed class DoSomethingController : CommandController

    [HttpPost("do-something")]
    public override Task<CausationIdResult> Handle(CancellationToken cancellationToken)
    
        // run logic here

        // generate a unique queue id

        // pass the queue id back to the client
        return Task.FromResult(CausationId(Guid.NewGuid()));
    

但是如果我希望从该控制器操作中获得其他参数怎么办?例如。读取参数、查询还是正文?

这是另一个展示可能实现的示例

[HttpPost("do-something/id")]
public override Task<CausationIdResult> Handle(CancellationToken cancellationToken, [FromRoute] string id)

    return Task.FromResult(CausationId(Guid.NewGuid()));

但显然这会导致编译错误,因为

没有合适的覆盖方法

是否可以像这样使用动态参数创建抽象方法?

public abstract Task<CausationIdResult> Handle(CancellationToken cancellationToken /* , a type that makes it possible to add all the additional parameters here */);

【问题讨论】:

【参考方案1】:

最适合的可能是params语法,但最后你不会弄清楚后续参数的含义。

为什么不采取完全不同的方法呢?就像使用 IDictionary 来存储 dynamic 参数一样。 动态,我的意思是CausationIdResult,它是孤立的,不知道注入其中的可能值。

为了使其工作,您可以使用动态(因此是键值对)数据类型创建CausationResult 的实例。

哦,C# 也支持dynamic types。

最后我相信你有一个 X->Y 问题。当您的问题是跟踪结果类的输入时,您正在尝试解决编译问题。

【讨论】:

【参考方案2】:

我会尝试解释您的问题并稍微调整解决方案。 原来的问题是:

应该始终返回带有 Guid 的状态代码 200 的操作

您正在尝试使用继承来解决这个问题,而这正是您遇到另一个问题的地方:

是否可以像这样使用动态参数创建抽象方法?

由于代码在 ASP.NET 核心堆栈中运行,我们现在可以使用更多工具来帮助我们解决最初的问题。如果您没有严格限制使用继承,则可以使用ASP.NET middleware 或ASP.NET MVC filters。 如examples 之一所述:

可以从控制器外部修改响应的内容。

你的过滤器看起来像这样:

public class HttpResponseCausationFilter : IActionFilter, IOrderedFilter

    protected CausationIdResult CausationId(Guid causationId) => new(causationId);    

    public int Order  get;  = int.MaxValue - 10;

    public void OnActionExecuting(ActionExecutingContext context)  

    public void OnActionExecuted(ActionExecutedContext context)
            
         context.Result = new ObjectResult(CausationId(Guid.NewGuid()))
         
            StatusCode = 200,
         ;  
    

别忘了注册过滤器

services.AddControllers(options =>
    options.Filters.Add(new HttpResponseCausationFilter()));

【讨论】:

以上是关于是否可以创建具有动态签名的抽象方法?的主要内容,如果未能解决你的问题,请参考以下文章

Java中带有抽象方法签名的可见性修饰符

重写方法,重载方法,虚方法和抽象方法的使用

为啥继承具有名称签名的接口成员的 C# 抽象类至少需要实现其中一个?

您可以强制从抽象基类继承的类仅具有在基本情况下定义的公共方法吗?

抽象类可否替代接口?

Java中的抽象类abstract