mardi 29 décembre 2020

Using reflection to instantiate an IEnumerable

I've built a simple extensible computation framework, where each class represents a different function for the framework.

This is a quick example of what I did:

BaseFunction:

namespace MyNamespace
{
    public abstract class BaseFunction
    {
        public abstract string Name { get; }

        public abstract int Index { get; }

        public long Execute()
        {
            Execute(ReadInput() /* out of scope */, out long result);
            return result;
        }

        internal abstract void Execute(string input, out long rResult);
    }
}

SampleFunction:

namespace MyNamespace.Code
{
    public class SampleFunction: BaseFunction
    {
        public override string Name => "Sample Function";

        public override int Index => 1;

        internal override void Execute(string input, out long result)
        {
            result = 0;
        }
    }
}

Using reflection, the framework also provided a CLI where the user can select its function and run it.

This is how all the functions are retrieved:

public static IEnumerable<BaseFunction> Functions()
{
    return GetTypesInNamespace(Assembly.GetExecutingAssembly(), "MyNamespace.Code")
        .Where(type => type.Name != "BaseFunction")
        .Select(type => (BaseFunction)Activator.CreateInstance(type))
        .OrderBy(type => type.Index);
}

and this is how the CLI is built:

var menu = new EasyConsole.Menu();
foreach (var day in FunctionsUtils.Functions())
{
    menu.Add(function.Name, () => function.Execute());
}

The framework works fine, but, as you can see, everything is a long now, and this takes us to my issue: I'd like to make the BaseFunction class generic, so that I can have different functions returning different type of values.

However, changing BaseFunction to BaseFunction<TResult> breaks the Functions method as I can't return a IEnumerable<BaseFunction>.

The logical next step is to add an interface, make BaseFunction implement the interface and add the generics to BaseFunction. This means that Functions can now return a IEnumerable<IBaseFunction>.

What still doesn't work, however, is the way I build the CLI menu: my interface must have the Execute method, and so we're back to square one: I can't add that method to my interface, because the return type is a generic and the interface doesn't have the generic reference.

Here I'm kind of stuck.

Is there any way to make this kind of framework work without changing all my return types to object (or maybe struct?) considering that I may also need to return non-numeric types?





Aucun commentaire:

Enregistrer un commentaire