我有一个通用接口和一堆实现它的类。下面是一个非常简单的例子:
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
创建某种弱类型的委托,但我完全失败了。这可能吗?
您可能已经知道具有此签名的方法: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
不安全 - 保持它的泛型,但在使用它时不要失去类型安全
我忘记分享我最终使用的解决方案了。它利用两个事实:
- 你可以很容易地在
- 您可以轻松地(cough
CreateDelegate
cough)使用typeof(T)
变量构建Generic<T>
类。
Generic<T>
类中转换为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);
}