![](/img/trans.png)
[英]Extension method on ObservableCollection<MyType>, MyType : IEnumerable<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.