使用反射实例化<MyType> IEnumerable,其中 MyType 具有泛型参数



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

这是我所做的一个快速例子:

基本函数

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,用户可以在其中选择其功能并运行它

这就是检索所有功能的方式:

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类通用,这样我就可以有不同的函数返回不同类型的值

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

合乎逻辑的下一步是添加一个接口,使BaseFunction实现该接口,并向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;
}

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

(我对BaseFunction做了一点修改,去掉了参数(

如果将BaseFunction更改为BaseFunction<TResult>

public abstract class BaseFunction<TResult>

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

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

更新:

提取一个基本接口以找到共性,然后在抽象类中设置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("nnPress 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;
}
}

}

最新更新