如何使用弱类型参数调用泛型方法

How to invoke a generic method with weak-typed parameter

我有一个通用接口和一堆 类 来实现它。这是一个大大简化的示例:

interface IPrinter<in T>
{
    void Print(T args);
}

class IntPrinter : IPrinter<int>
{
    public void Print(int args)
    {
        Console.WriteLine("This is int: " +args);
    }
}

class StringPrinter : IPrinter<string>
{
    public void Print(string args)
    {
        Console.WriteLine("This is string: " + args);
    }
}

我还有一本 类 以通用参数类型作为键的字典(我实际上使用反射来填充它):

private readonly Dictionary<Type, object> Printers 
    = new Dictionary<Type, object>
        {
            {typeof (int), new IntPrinter()},
            {typeof (string), new StringPrinter()}
        };

现在我收到一个List<object>的实例作为输入,它包含一堆任意类型的参数。对于每个参数,我想选择对象,实现适当的接口并调用 print 方法。我不确定如何实现它。

var args = new List<object> {1, "2"};
foreach (var arg in args)
{
    var printer = Printers[arg.GetType()];
    //how do I implement this method so it calls ((IPrinter<T>)printer).Print((T)arg)?
    Print(printer, arg); 
}

我试过了

  1. 反思。它有效,但很好......它的反映。我正在寻找其他方法。

    private void Print(object printer, object arg)
    {
        printer.GetType().GetMethod("Print").Invoke(printer, new[] {arg});
    }
    
  2. 动态。比反射更干净,通常更快。但是出于某种原因,如果我将它与一种类型一起使用,它会抛出异常,该类型相对于执行程序集是 private 。这是动态对象的已知限制吗?

    private void Print(dynamic printer, dynamic arg)
    {
        printer.Print(arg);
    }
    
  3. 代表。我试图使用 CreateDelegate 方法从 MethodInfo 创建某种弱类型的委托,但我完全失败了。有可能吗?

这会有帮助吗?

private void Print<T, TArgs>(T printer, TArgs arg) where T : IPrinter<TArgs>
{
    printer.Print(arg);
}

您可能已经知道具有此签名的方法:void GenericMethod<T>(T arg) 将被翻译成类似的东西:void __GenericMethod(Type typeArg1, object argument) 在第一次调用 class 类型参数后(对于结构,您将为每个结构生成一个)。

想一想。为什么我们需要泛型?为什么不简单地拥有:

interface IPrinter
{
    void Print(object args);
}

甚至...

interface IAnything
{
    void Do(object args);
}

答案是:编译时 C# 的特性 - 类型安全。我们需要它来避免意外地组合不兼容的组件。您不希望您的 IntPrinter 以某种方式收到 string。幸运的是你会得到一个例外,但如果没有呢?

所以,使用这个功能很好,可以帮助我们在编译时检测错误。但是你收集 Printers 词典是为了做什么? 你正在失去类型安全。如果你在某处的类型上犯了错误,你将在运行时遇到异常或程序的其他奇怪行为。

我建议你两个选择:

  • 使IPrinter类型不安全
  • 保持通用,但在使用时不要失去类型安全性

我忘了分享我最终使用的解决方案。它利用了两个事实:

  1. 您可以在 Generic<T> class.
  2. 中轻松转换为 T
  3. 您可以轻松地 (coughCreateDelegatecough) 使用 Generic<T> class =15=]变量。

LinqPad 示例:

interface IPrinter<in T>
{
    void Print(T args);
}

class IntPrinter : IPrinter<int>
{
    public void Print(int args)
    {
        Console.WriteLine("This is int: " +args);
    }
}

class StringPrinter : IPrinter<string>
{
    public void Print(string args)
    {
        Console.WriteLine("This is string: " + args);
    }
}

interface IPrintInvoker
{
    void Print(object printer, object args);
}

class PrintInvoker<T> : IPrintInvoker
{
    public void Print(object printer, object args)
    {
         ((IPrinter<T>)printer).Print((T)args);
    }
}

private readonly Dictionary<Type, IPrintInvoker> Invokers = new Dictionary<Type, IPrintInvoker>();

private void Print(object printer, object arg)
{
     var type = arg.GetType();
    IPrintInvoker invoker;
    if (!Invokers.TryGetValue(type, out invoker))
    {
        var invokerType = typeof(PrintInvoker<>).MakeGenericType(type);
        Invokers[type] = invoker = (IPrintInvoker)Activator.CreateInstance(invokerType );
    }
    invoker.Print(printer, arg);
}

void Main()
{
   var printer1 = new IntPrinter();
   var printer2 = new StringPrinter();
   Print(printer1, 1);
   Print(printer1, 2);
   Print(printer2, "asfasfasf");
}