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

时间:2015-07-14 14:11:15

标签: c# generics reflection

我有一个通用接口和一堆实现它的类。这是一个非常简化的例子:

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创建某种弱类型的委托,但我完全失败了。它甚至可能吗?

3 个答案:

答案 0 :(得分:1)

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

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

interface IPrinter
{
    void Print(object args);
}

甚至......

interface IAnything
{
    void Do(object args);
}

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

因此,使用此功能很好,可以帮助我们在编译时检测错误。但是你通过收集Printers字典做了什么? 您失去了类型安全性。如果您在某处出现类型错误,您将在运行时遇到异常或程序的其他奇怪行为。

我建议你两个选择:

  • 使IPrinter无法安全
  • 保持通用,但在使用时不要放松类型安全

答案 1 :(得分:1)

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

  1. 您可以轻松地在T课程内投射到Generic<T>
  2. 您可以使用CreateDelegate变量轻松(咳嗽 Generic<T> 咳嗽)构建typeof(T)课程。
  3. 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");
    }
    

答案 2 :(得分:-1)

这会有帮助吗?

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