Generic Base Class Overriding Non-Generic Base Class Function Pattern? (.NET)

iam1me

I'm wondering if anyone has a good suggestion/pattern in mind for solving the following design issue. I have a heirarchy of command classes. At the most abstract level I have an ICommand interface. The result from executing an ICommand's RunCommand() function is an Object. Different commands will have different result types, so this is an appropriate abstraction.

Building the heirarchy out a bit more, it becomes desirable to use generics. I create a Generic.ICommand(Of TResult) interface.

There's some common boiler plate code like Run, TryRun, BeginRun, BeginTryRun, etc that I want for all commands - so I create a BaseCommand class that provides all this, and which implements the non-generic ICommand interface. The BaseCommand doesn't know how to actually execute anything however, so all of these commands ultimately invoke a protected abstract function called InternalRunCommand. This all works beautifully.

Now I want to create a generic version of that class: BaseCommand(Of T). It inherits from BaseCommand and also implements the generic ICommand(Of T) interface. This works, but now there is a discrepancy: The InternalRunCommand.

In the non-generic version InternalRunCommand returns an Object. In my generic BaseCommand(Of T) class I'd like to overload that with a generic version that returns the result as type T. Unfortunately, the VB.NET/C# compilers don't let you provide an overload of a method where the only difference is the return type.

Since this is a protected function, it ultimately doesn't make much difference to the overall API, but it nevertheless irritates me that I don't have an aesthetically pleasing solution to this bit of architecture.

For the time being I have overridden the non-generic InternalRunCommand in the BaseCommand(Of T) class so that it invokes a new protected, abstract OnRunCommand function that takes the same parameters but returns a result of type T. The InternalRunCommand has also been declared NonOverridable. This maybe the closest I can get - but wanted to see if there are any better ideas out there? :)

EDIT: I've included a simplified copy of the code as requested so that you may better visualize the problem:

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
iam1me

I think I've found a good pattern that allows you to override the non-generic version of such functions with generic ones, and without any messy wrapper functions like I had in the OP.

I've replaced the protected, abstract InnerRunCommand function with a protected, ReadOnly property of the same name. The type of this property is Func(Of ICommand, Device, Object). I modified the constructor of the BaseCommand class to accept such a Func object.

In the Generic.BaseCommand(Of T) class, I can Shadow the InnerRunCommand with a similar property of type Func(Of ICommand, Device, T). The constructor for the Generic.BaseCommand(Of T) similarly accepts such a Func object, and it passes that object back to the non-generic BaseCommand constructor without issue :)

I'm in the processing of modifying my architecture to support this new pattern, and will let you all know if I run into any problems. I welcome any critiques to this approach and welcome alternate answers :)

EDIT: I have constructed a simple example of the proposed pattern below. I have written it in C# instead of VB.NET. There is a little more work involved in casting the FUNC objects in C# (VB.NET handles this for you behind the scenes). In both languages, however, the use of these anonymous functions keeps the API clean and extensible.

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

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

Casting non generic class to generic base class

From Dev

Overriding generic methods from base class

From Dev

Using generic function in base class

From Dev

Generic Entity Base Class

From Dev

casting to base generic class

From Dev

Generic base class constructor

From Dev

Invoking generic method/delegate from a non-generic base class

From Dev

define a generic property in non-generic base class

From Dev

Pattern for exposing non generic and generic Class

From Dev

Will work these code the same (Generic Class with base class constraint vs Non-generic Class "counterpart")

From Dev

Generalization of class in generic function creates object code for base class?

From Dev

How to cast into generic base class

From Dev

convert generic type to base class

From Dev

Typescript: Generic type in base class

From Dev

Accessing a method on a generic base class

From Dev

Overriding equals with generic class

From Dev

Set a generic class reference to a base class, where the generic class's generic parameter extends that base class

From Dev

MongoDB C# driver type discriminators with generic class inheriting from non-generic base class

From Dev

C# cast non-generic class to generic base class regardless of type T

From Dev

Returning derived class as base generic class

From Dev

How to set the base class property of a generic class

From Dev

Use generic class for generic parameter base with out generic parameter

From Dev

Show Base Class throws error if base class is generic and in a class library

From Dev

Function returns generic class of base interface - Type mismatch

From Java

Generic method overriding a generic method of its base

From Dev

Calling the overriding function through a reference of base class

From Dev

MVC View Scaffolding not working with Generic base class?

From Dev

Infer generic parameter from base class

From Dev

Enum with generic base class passed as contructor parameter

Related Related

  1. 1

    Casting non generic class to generic base class

  2. 2

    Overriding generic methods from base class

  3. 3

    Using generic function in base class

  4. 4

    Generic Entity Base Class

  5. 5

    casting to base generic class

  6. 6

    Generic base class constructor

  7. 7

    Invoking generic method/delegate from a non-generic base class

  8. 8

    define a generic property in non-generic base class

  9. 9

    Pattern for exposing non generic and generic Class

  10. 10

    Will work these code the same (Generic Class with base class constraint vs Non-generic Class "counterpart")

  11. 11

    Generalization of class in generic function creates object code for base class?

  12. 12

    How to cast into generic base class

  13. 13

    convert generic type to base class

  14. 14

    Typescript: Generic type in base class

  15. 15

    Accessing a method on a generic base class

  16. 16

    Overriding equals with generic class

  17. 17

    Set a generic class reference to a base class, where the generic class's generic parameter extends that base class

  18. 18

    MongoDB C# driver type discriminators with generic class inheriting from non-generic base class

  19. 19

    C# cast non-generic class to generic base class regardless of type T

  20. 20

    Returning derived class as base generic class

  21. 21

    How to set the base class property of a generic class

  22. 22

    Use generic class for generic parameter base with out generic parameter

  23. 23

    Show Base Class throws error if base class is generic and in a class library

  24. 24

    Function returns generic class of base interface - Type mismatch

  25. 25

    Generic method overriding a generic method of its base

  26. 26

    Calling the overriding function through a reference of base class

  27. 27

    MVC View Scaffolding not working with Generic base class?

  28. 28

    Infer generic parameter from base class

  29. 29

    Enum with generic base class passed as contructor parameter

HotTag

Archive