我有一个通用接口和一堆实现它的类。这是一个非常简化的例子:
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
创建某种弱类型的委托,但我完全失败了。它甚至可能吗?
答案 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)
我忘了分享我最终使用的解决方案。它利用了两个事实:
T
课程内投射到Generic<T>
。CreateDelegate
变量轻松(咳嗽 Generic<T>
咳嗽)构建typeof(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");
}
答案 2 :(得分:-1)
这会有帮助吗?
private void Print<T, TArgs>(T printer, TArgs arg) where T : IPrinter<TArgs>
{
printer.Print(arg);
}