c# - 泛型基类覆盖非泛型基类函数模式? (。网)

标签 c# .net vb.net generics design-patterns

我想知道是否有人对解决以下设计问题有好的建议/模式。我有一个命令类的层次结构。在最抽象的层面上,我有一个 ICommand 接口(interface)。执行 ICommand 的 RunCommand() 函数的结果是一个对象。不同的命令将有不同的结果类型,因此这是一个适当的抽象。

进一步构建层次结构,使用泛型变得可取。我创建了一个 Generic.ICommand(Of TResult) 接口(interface)。

有一些常见的样板代码,如 Run、TryRun、BeginRun、BeginTryRun 等,我需要所有命令 - 所以我创建了一个 BaseCommand 类来提供所有这些,并实现了非通用 ICommand 接口(interface)。然而,BaseCommand 不知道如何实际执行任何操作,因此所有这些命令最终都会调用一个名为 InternalRunCommand 的 protected 抽象函数。这一切都很好。

现在我想创建该类的通用版本:BaseCommand(Of T)。它继承自 BaseCommand 并且还实现了通用 ICommand(Of T) 接口(interface)。这行得通,但现在存在差异:InternalRunCommand。

在非通用版本中,InternalRunCommand 返回一个对象。在我的通用 BaseCommand(Of T) 类中,我想用一个通用版本重载它,该版本将结果作为类型 T 返回。不幸的是,VB.NET/C# 编译器不允许您提供方法重载,其中唯一的区别是返回类型。

由于这是一个 protected 函数,它最终不会对整个 API 产生太大影响,但它仍然让我恼火,因为我没有针对这一点架构的美观解决方案。

目前我已经覆盖了 BaseCommand(Of T) 类中的非泛型 InternalRunCommand,以便它调用一个新的 protected 抽象 OnRunCommand 函数,该函数采用相同的参数但返回类型 T 的结果。InternalRunCommand 具有也被声明为 NonOverridable。这可能是我能得到的最接近的 - 但想看看是否有更好的想法? :)

编辑:我已按要求包含了代码的简化副本,以便您可以更好地想象问题:

Public Interface ICommand
    Property Name as String
    Property Description As String
    Property ResultType as Type
    Function RunCommand(target as Device) As Object
    Function TryRunCommand(target as Device, Byref result as Object) AS Boolean
    Function BeginRunCommand(target as Device) as Task(Of Object)
    Function BeginTryRunCommand(target as Device) As Task(of Boolean)
End Interface

Namespace Generic
Public Interface ICommand(Of TResult)
    Function RunCommand(target as Device) as T
    Function BeginRunCommand(target as Device) As Task(Of T)
End Interface
End Namespace

Public MustInherit Class BaseCommand
    Implements ICommand

    Public Function RunCommand(target as Device) As Object Implements ICommand.RunCommand
        Return InternalRunCommand(device)
    End Function

    Public Function BeginRunCommand(target as Device) As Task(of Object) Implements ICommand.BeginRunCommand
        Return Task(Of Object).Factory.StartNew( Function() InternalRunCommand(target))
    End Function

    ' Other boiler plate code goes here'

    Protected MustOverride Function InternalRunCommand(target as Device) As Object

End Class

Namespace Generic
Public Class BaseCommand(Of TResult)
    Inherits BaseCommand
    Implements ICommand(Of TResult)

    Public Function BeginRunCommand(target as Device) As Task(of TResult) Implements ICommand(Of TResult).BeginRunCommand
        Return Task(Of TResult).Factory.StartNew( Function() OnRunCommand(target))
    End Function

    Protected NotOverridable Overrides Function InternalRunCommand(target as Device) As Object
        ' Re-route to the generic version'
        Return OnRunCommand(device)
    End Function

    Protected MustOverride Function OnRunCommand(target as Device) As T
End Class

最佳答案

我想我已经找到了一个很好的模式,它允许您使用通用函数覆盖此类函数的非通用版本,并且没有像我在 OP 中那样的任何困惑的包装函数。

我已将 protected 抽象 InnerRunCommand 函数替换为同名的 protected ReadOnly 属性。此属性的类型是 Func(Of ICommand, Device, Object)。我修改了 BaseCommand 类的构造函数以接受这样一个 Func 对象。

在 Generic.BaseCommand(Of T) 类中,我可以使用 Func(Of ICommand, Device, T) 类型的类似属性隐藏 InnerRunCommand。 Generic.BaseCommand(Of T) 的构造函数同样接受这样一个 Func 对象,并将该对象毫无问题地传递回非泛型 BaseCommand 构造函数:)

我正在修改我的架构以支持这种新模式,如果我遇到任何问题,我会告诉大家。我欢迎对这种方法的任何批评,并欢迎其他答案:)

编辑:我在下面构造了一个建议模式的简单示例。我用 C# 而不是 VB.NET 编写了它。在 C# 中转换 FUNC 对象涉及更多的工作(VB.NET 在幕后为您处理)。然而,在这两种语言中,这些匿名函数的使用保持了 API 的清洁和可扩展性。

public interface ICommand
{
    object Execute();
    Boolean TryExecute(out object result);
    Task<object> BeginExecute();        
}

namespace Generic
{
    public interface ICommand<TResult> : ICommand
    {
        new TResult Execute();
        Boolean TryExecute(out TResult result);
        new Task<TResult> BeginExecute();
    }
}

public class Command : ICommand
{
    private Func<ICommand, object> _execFunc = null;
    protected Func<ICommand, object> ExecFunc { get { return _execFunc; } }

    public Task<object> BeginExecute()
    {
        return Task<object>.Factory.StartNew(() => _execFunc(this) );
    }

    public object Execute()
    {
        return _execFunc(this);
    }

    public bool TryExecute(out object result)
    {
        try
        {
            result = _execFunc(this);
            return true;
        }
        catch(Exception ex)
        {
            result = null;
            return false;
        }
    }

    public Command (Func<ICommand, object> execFunc)
    {
        if (execFunc == null) throw new ArgumentNullException("execFunc");
        _execFunc = execFunc;
    }
}

namespace Generic
{
    public class Command<TResult> : Command, ICommand<TResult> where TResult : class            
    {
        new protected Func<ICommand<TResult>, TResult> ExecFunc => (ICommand<TResult> cmd) => (TResult)base.ExecFunc(cmd);

        public bool TryExecute(out TResult result)
        {
            try
            {
                result = ExecFunc(this);
                return true;
            }
            catch(Exception ex)
            {
                result = null;
                return false;
            }
        }

        Task<TResult> ICommand<TResult>.BeginExecute()
        {
            return Task<TResult>.Factory.StartNew(() => ExecFunc(this) );
        }

        TResult ICommand<TResult>.Execute()
        {
            return ExecFunc(this);
        }

        public Command(Func<ICommand<TResult>, TResult> execFunc) : base((ICommand c) => (object)execFunc((ICommand<TResult>)c))
        {
        }
    }
}

public class ConcatCommand : Generic.Command<string> 
{
    private IEnumerable<string> _inputs;
    public IEnumerable<String> Inputs => _inputs;

    public ConcatCommand(IEnumerable<String> inputs) : base( (Generic.ICommand<string> c) => (string)String.Concat(((ConcatCommand)c).Inputs) )
    {
        if (inputs == null) throw new ArgumentNullException("inputs");
        _inputs = inputs;
    }

}

class Program
{
    static void Main(string[] args)
    {
        string[] inputs = { "This", " is ", " a ", " very ", " fine ", " wine!" };
        ICommand c = new ConcatCommand(inputs );
        string results = (string)c.Execute();
        Console.WriteLine(results);
        Console.ReadLine();
    }
}

关于c# - 泛型基类覆盖非泛型基类函数模式? (。网),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52846094/

相关文章:

c# - 解析文件中的Net Core Catch语句和其他业务逻辑

c# - 通过 ASP.NET 3.5 C# 方法插入表时返回异常

c# - C# 中基于事件的异步;任何可能的通用重构吗?

c# - 从 ASP.Net MVC 5 Identity 添加外键时出错?

c# - DataGridView,水平滚动时始终保持第一列可见?

c# - AttributeUsageAtribute 是如何实现的?

c# - 字符串到日期时间转换失败

.net - 程序集中的类列表

c# - 可以设置RequestValidationMode ="2.0"页面级别吗?

c# - 为什么 element.getAttribute ("style") 忽略冒号 ":"字符之后的所有内容?仅返回部分值