[英]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.