繁体   English   中英

使用反射实例化一个 IEnumerable<mytype> 其中 MyType 有一个通用参数</mytype>

[英]Using reflection to instantiate an IEnumerable<MyType> where MyType has a generic parameter

我构建了一个简单的可扩展计算框架,其中每个 class 代表该框架的不同 function。

这是我所做的一个简单示例:

基函数

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

样品功能

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

使用反射,该框架还提供了一个 CLI,用户可以在其中 select 和 function 并运行它。

这是检索所有函数的方式:

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

这就是 CLI 的构建方式:

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

该框架运行良好,但是,正如您所看到的,现在一切都很long ,这将我们带到了我的问题:我想让BaseFunction class 通用,这样我就可以让不同的函数返回不同类型的值。

但是,将BaseFunction更改为BaseFunction<TResult>会破坏Functions方法,因为我无法返回IEnumerable<BaseFunction>

合乎逻辑的下一步是添加一个接口,使BaseFunction实现该接口并将 generics 添加到BaseFunction 这意味着Functions现在可以返回IEnumerable<IBaseFunction>

然而,仍然不起作用的是我构建 CLI 菜单的方式:我的界面必须有Execute方法,所以我们回到第一个问题:我不能将该方法添加到我的界面,因为返回类型是泛型的,接口没有泛型引用。

在这里,我有点卡住了。

考虑到我可能还需要返回非数字类型,有什么方法可以在不将我所有的返回类型更改为object (或者可能是struct ?)的情况下使这种框架工作?

假设输入和结果可以是任何东西,你需要这样的东西:

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

        public abstract int Index { get; }

        public object Execute() => Execute(ReadInput());

        private object ReadInput()
        {
            // out of scope
            return null;
        }

        protected abstract object Execute(object input);
    }

    public abstract class BaseFunction<TInput, TResult> : BaseFunction
    {
        protected sealed override object Execute(object input) => Execute(ConvertInput(input));

        protected abstract TInput ConvertInput(object input);

        protected abstract TResult Execute(TInput input);
    }

    public sealed class SampleFunction : BaseFunction<string, long>
    {
        public override string Name => "Returns string length";

        public override int Index => 0;

        protected override string ConvertInput(object input) => (string)input;

        protected override long Execute(string input) => input.Length;
    }

在实现特定的 function 时,这仍然允许您将函数组合到IEnumerable<BaseFunction>中并执行它们,而且还允许使用强类型输入和结果。

(我稍微修改了BaseFunction以丢弃参数)

如果将 BaseFunction 更改为 BaseFunction<TResult>

    public abstract class BaseFunction<TResult>

那么为什么不直接更改函数的签名以返回 BaseFunction <TResult>?

public static class FunctionClass<TResult>
{
    public static IEnumerable<BaseFunction<TResult>> Functions()
    {

更新:

提取基本接口以找到共性,然后在抽象 class 中设置 TResult 似乎可行。 我还稍微改进了 linq 查询。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace MyNamespace
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            var list = FunctionClass.Functions();
        foreach(var item in list)
        {
            Console.WriteLine($"{item.Name} - {item.GetType().Name}");
            if(item is BaseFunction<int>)
            {
                Console.WriteLine($"int result {((BaseFunction<int>)item).Execute()}");
            }
            if (item is BaseFunction<long>)
            {
                Console.WriteLine($"long result {((BaseFunction<long>)item).Execute()}");
            }
        }
        Console.WriteLine("\n\nPress Any Key to Close");
        Console.ReadKey();
    }
}

public class FunctionClass
{

    private static Type[] GetTypesInNamespace(Assembly assembly, string nameSpace)
    {
        return
          assembly.GetTypes()
                  .Where(t => String.Equals(t.Namespace, nameSpace, StringComparison.Ordinal))
                  .ToArray();
    }

    public static IEnumerable<IBaseFunction> Functions()
    {
        return GetTypesInNamespace(Assembly.GetExecutingAssembly(), "MyNamespace.Code")
            .Where(type => type.IsClass && typeof(IBaseFunction).IsAssignableFrom(type))
            .Select(type => (IBaseFunction)Activator.CreateInstance(type))
            .OrderBy(type => ((IBaseFunction)type).Index);
    }
}
}

namespace MyNamespace
{
    public interface IBaseFunction
    {
        public string Name { get; }

        public long Index { get; }

    }
public abstract class BaseFunction<TResult> : IBaseFunction
{
    public virtual string Name { get; }

    public virtual long Index { get; }

    public TResult Execute()
    {
        Execute("foo" /* out of scope */, out TResult result);
        return result;
    }

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

namespace MyNamespace.Code
{
    public class SampleFunction : BaseFunction<int>
    {
        public override string Name => "Sample Function1 - with int";

        public override long Index => 1;

    internal override void Execute(string input, out int rResult)
    {
        rResult = 0;
    }
}

public class SampleFunction2 : BaseFunction<long>
{
    public override string Name => "Sample Function2 - with long";

    public override long Index => 1;

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

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM