簡體   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