如何使用弱类型参数调用泛型方法
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);
}
我试过了
反思。它有效,但很好......它的反映。我正在寻找其他方法。
private void Print(object printer, object arg)
{
printer.GetType().GetMethod("Print").Invoke(printer, new[] {arg});
}
动态。比反射更干净,通常更快。但是出于某种原因,如果我将它与一种类型一起使用,它会抛出异常,该类型相对于执行程序集是 private
。这是动态对象的已知限制吗?
private void Print(dynamic printer, dynamic arg)
{
printer.Print(arg);
}
代表。我试图使用 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
类型不安全
- 保持通用,但在使用时不要失去类型安全性
我忘了分享我最终使用的解决方案。它利用了两个事实:
- 您可以在
Generic<T>
class. 中轻松转换为 T
- 您可以轻松地 (cough
CreateDelegate
cough) 使用 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");
}
我有一个通用接口和一堆 类 来实现它。这是一个大大简化的示例:
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);
}
我试过了
反思。它有效,但很好......它的反映。我正在寻找其他方法。
private void Print(object printer, object arg) { printer.GetType().GetMethod("Print").Invoke(printer, new[] {arg}); }
动态。比反射更干净,通常更快。但是出于某种原因,如果我将它与一种类型一起使用,它会抛出异常,该类型相对于执行程序集是
private
。这是动态对象的已知限制吗?private void Print(dynamic printer, dynamic arg) { printer.Print(arg); }
代表。我试图使用
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
类型不安全 - 保持通用,但在使用时不要失去类型安全性
我忘了分享我最终使用的解决方案。它利用了两个事实:
- 您可以在
Generic<T>
class. 中轻松转换为 - 您可以轻松地 (cough
CreateDelegate
cough) 使用Generic<T>
class =15=]变量。
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");
}