【问题标题】:Using string as parameter for type when calling constructor调用构造函数时使用字符串作为类型的参数
【发布时间】:2020-05-28 07:57:03
【问题描述】:

我正在尝试基于字符串参数创建类型并将其传递给构造函数的类型参数。仅使用 if 语句检查它会变得非常讨厌,而且我不知道如何以编程方式/通用方式进行更多操作。

我尝试过反射,但只返回一个对象并将一个对象传递给 显然是行不通的。

有没有人知道如何在没有数千个 if 语句的情况下以更微妙的方式解决这个问题?

对象创建如下所示:

                if (Options.Input1Type == "int" && Options.Output1Type == "int") return BlockBuilder.Build<int, int>(Kind, Options, TransformToSelf);
                if (Options.Input1Type == "bool" && Options.Output1Type == "bool") return BlockBuilder.Build<bool, bool>(Kind, Options, TransformToSelf);
                if (Options.Input1Type == "string" && Options.Output1Type == "string") return BlockBuilder.Build<string, string>(Kind, Options, TransformToSelf);

                if (Options.Input1Type == "bool" && Options.Output1Type == "int") return BlockBuilder.Build<bool, int>(Kind, Options, TransformToInt);
                if (Options.Input1Type == "bool" && Options.Output1Type == "string") return BlockBuilder.Build<bool, string>(Kind, Options, TransformToString);

                if (Options.Input1Type == "int" && Options.Output1Type == "bool") return BlockBuilder.Build<int, bool>(Kind, Options, TransformToBool);
                if (Options.Input1Type == "int" && Options.Output1Type == "string") return BlockBuilder.Build<int, string>(Kind, Options, TransformToString);

                if (Options.Input1Type == "string" && Options.Output1Type == "int") return BlockBuilder.Build<string, int>(Kind, Options, TransformToInt);
                if (Options.Input1Type == "string" && Options.Output1Type == "bool") return BlockBuilder.Build<string, bool>(Kind, Options, TransformToBool);

BlockBuilder 看起来像这样:

public static IDataflowBlock Build<TIn, TOut>(string kind, BlockOptions blockOptions, Func<TIn, TOut> singleOutputExecutionFunction = null, Func<TIn, IEnumerable<TOut>> multipleOutputExecutionFunction = null)
    {
        if (singleOutputExecutionFunction == null && multipleOutputExecutionFunction == null)
            throw new ArgumentException("Missing function to execute");

        Enum.TryParse(kind, out TransformationBlocks Kind);

        switch (Kind)
        {
            case TransformationBlocks.Undefined:
                throw new ArgumentException("No block type was specified");
            case TransformationBlocks.TransformBlock:
                return new TransformBlock<TIn, TOut>(param => { return singleOutputExecutionFunction(param); }, new ExecutionDataflowBlockOptions()
                {
                    MaxMessagesPerTask = blockOptions.MaxMessagesPerTask,
                    BoundedCapacity = blockOptions.BoundedCapacity,
                    MaxDegreeOfParallelism = blockOptions.MaxDegreeOfParallelism,
                });
            case TransformationBlocks.TransformManyBlock:
                return new TransformManyBlock<TIn, TOut>(param => { return multipleOutputExecutionFunction(param); }, new ExecutionDataflowBlockOptions()
                {
                    MaxMessagesPerTask = blockOptions.MaxMessagesPerTask,
                    BoundedCapacity = blockOptions.BoundedCapacity,
                    MaxDegreeOfParallelism = blockOptions.MaxDegreeOfParallelism,
                });
            default:
                return default;
        }
    }

委托/函数如下所示:

    private static T TransformToSelf<T>(T obj)
    {
        return obj;
    }

    private static string TransformToString<T>(T obj)
    {
        return Convert.ToString(obj);
    }

    private static int TransformToInt<T>(T obj)
    {
        return Convert.ToInt32(obj);
    }

    private static bool TransformToBool<T>(T obj)
    {
        return Convert.ToBoolean(obj);
    }

【问题讨论】:

  • 用查找表替换那些if 语句会更好,这样它就不会看起来那么糟糕/重复。此外,C#8(或者可能是 9)对这种模式有更好的 switch/case 处理
  • 顺便说一句,您可能应该将TransformXX 方法中的T 限制为IConvertible,否则您将失去它可以与Convert 类一起使用的保证。此时,您可以通过静态方法调用接口上的方法。您还可以将所有这些方法简化为对(T)Convert.ChangeType(obj, typeof(T)) 的一次调用,这要求类型为IConvertible。您也应该能够约束 TInTOut ——这可能对整体解决方案有帮助,也可能没有帮助
  • 感谢两位的提示!我利用了它们并设法以一种非常优雅的方式做到了这一点。

标签: c# generics types task-parallel-library dataflow


【解决方案1】:

这并不容易,但它是可行的。

如果您可以将Input1Type 和 Input2Type 的类型更改为 System.Type 而不是字符串,那就容易多了。

如果没有,那么我建议您创建一个 @neil 建议的映射函数,将字符串映射到类型,然后使用 MethodInfo.MakeGenericType() 调用您的 Build() 函数。

请参阅下面的MakeGenericType() 的简单示例。

using System;
using System.Reflection;

namespace make_generic_type
{
    class Program
    {
        static void Main(string[] args)
        {
            // Normal C# usage
            var host = new Host();
            Console.WriteLine(host.GenericMethod<int, string>("Test"));

            // Use reflection to get type definition
            var unboundMethod = typeof(Host).GetMethod(nameof(Host.GenericMethod));
            // As the method is generic, you need to pass the type parameters in.
            // We do this by binding the type parameters with MethodInfo.MakeGenericMethod();
            var boundMethod = unboundMethod.MakeGenericMethod(new Type[]{ typeof(int), typeof(string) });

            // Now we have a method that we can invoke via reflection as normal
            Console.WriteLine(boundMethod.Invoke(new Host(), new object[]{ "Test"}));
        }


    }

    class Host{
        public string GenericMethod<TIn, TOut>(string kind)
        {
            return $"{typeof(TIn).Name}; {typeof(TOut).Name}; {kind};";
        }
    }
}

【讨论】:

  • 非常感谢您的回答!通过更多的研究,我设法通过使用 MakeGenericType() 和 Delegate.CreateDelegate() 来完成我打算做的事情
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-15
相关资源
最近更新 更多