如何调用带有弱类型参数的泛型方法



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

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

您可能已经知道具有此签名的方法: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不安全
  • 保持它的泛型,但在使用它时不要失去类型安全

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

    你可以很容易地在Generic<T>类中转换为T
  1. 您可以轻松地(cough CreateDelegate cough)使用typeof(T)变量构建Generic<T>类。

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");
}

这有帮助吗?

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

相关内容

  • 没有找到相关文章

最新更新