Nikita B Nikita B -4 years ago 59
C# Question

How to invoke a generic method with weak-typed parameter

I've got a generic interface and bunch of classes which implement it. Here is a greatly simplified example:

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


I also have a dictionary of those classes with generic argument's type as a key (I actually use reflection to populate it):

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


Now I recive an instance of
List<object>
as an input, which contains a bunch of parameters of arbitrary type. For each parameter I want to pick the object, which implement an appropriate interface and call the print method. I am not sure how to implement that.

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


I have tried


  1. Reflection. It works, but well... its reflection. I am looking for other ways to do it.

    private void Print(object printer, object arg)
    {
    printer.GetType().GetMethod("Print").Invoke(printer, new[] {arg});
    }

  2. Dynamic. A lot cleaner than reflection and usually faster. But for some reason it throws an exception if I use it with a type, which is
    private
    relative to executing assembly. Is this a known limitation of dynamic objects?

    private void Print(dynamic printer, dynamic arg)
    {
    printer.Print(arg);
    }

  3. Delegate. I was trying to use
    CreateDelegate
    method to create a some kind of weak-typed delegate from
    MethodInfo
    but i completely failed at that. Is it even possible?


Answer Source

I forgot to share the solution which I ended up using. It utilizes two facts:

  1. You can easily cast to T inside Generic<T> class.
  2. You can easily (coughCreateDelegatecough) construct Generic<T> class using typeof(T) variable.

LinqPad sample:

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 IPrintWrapper 
{
    void Print(object printer, object args);
}

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

private readonly Dictionary<Type, IPrintWrapper> Wrappers = new Dictionary<Type, IPrintWrapper>();

private void Print(object printer, object arg)
{
     var type = arg.GetType();
    IPrintWrapper wrapper;
    if (!Wrappers.TryGetValue(type, out wrapper))
    {
        var wrapperType = typeof(PrintWrapper<>).MakeGenericType(type);
        Wrappers[type] = wrapper = (IPrintWrapper)Activator.CreateInstance(wrapperType);
    }
    wrapper.Print(printer, arg);
}

void Main()
{
   var printer1 = new IntPrinter();
   var printer2 = new StringPrinter();
   Print(printer1, 1);
   Print(printer1, 2);
   Print(printer2, "asfasfasf");
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download