我正在为我的 IoC 类库重写我的流利界面,当我重构一些代码以便通过基类共享一些通用功能时,我遇到了一个障碍。
注意:这是我想做的事情,而不是我必须做的事情。如果我不得不使用不同的语法,我会的,但如果有人知道如何让我的代码按照我想要的方式编译,那将是最受欢迎的。
我希望某些扩展方法可用于特定的基类,并且这些方法应该是通用的,具有一种通用类型,与方法的参数相关,但这些方法还应该返回与它们的特定后代相关的特定类型'被调用。
使用代码示例比上面的描述更好。
这是一个简单而完整的例子,说明什么不起作用:
using System;
namespace ConsoleApplication16
{
public class ParameterizedRegistrationBase { }
public class ConcreteTypeRegistration : ParameterizedRegistrationBase
{
public void SomethingConcrete() { }
}
public class DelegateRegistration : ParameterizedRegistrationBase
{
public void SomethingDelegated() { }
}
public static class Extensions
{
public static ParameterizedRegistrationBase Parameter<T>(
this ParameterizedRegistrationBase p, string name, T value)
{
return p;
}
}
class Program
{
static void Main(string[] args)
{
ConcreteTypeRegistration ct = new ConcreteTypeRegistration();
ct
.Parameter<int>("age", 20)
.SomethingConcrete(); // <-- this is not available
DelegateRegistration del = new DelegateRegistration();
del
.Parameter<int>("age", 20)
.SomethingDelegated(); // <-- neither is this
}
}
}
如果你编译这个,你会得到:
'ConsoleApplication16.ParameterizedRegistrationBase' does not contain a definition for 'SomethingConcrete' and no extension method 'SomethingConcrete'...
'ConsoleApplication16.ParameterizedRegistrationBase' does not contain a definition for 'SomethingDelegated' and no extension method 'SomethingDelegated'...
我想要的是扩展方法 ( Parameter<T>
) 能够在ConcreteTypeRegistration
和上调用DelegateRegistration
,并且在这两种情况下,返回类型都应该与调用扩展的类型相匹配。
问题如下:
我想写:
ct.Parameter<string>("name", "Lasse")
^------^
notice only one generic argument
但也Parameter<T>
返回一个与它被调用的类型相同的对象,这意味着:
ct.Parameter<string>("name", "Lasse").SomethingConcrete();
^ ^-------+-------^
| |
+---------------------------------------------+
.SomethingConcrete comes from the object in "ct"
which in this case is of type ConcreteTypeRegistration
有什么办法可以欺骗编译器为我实现这一飞跃?
如果我向方法中添加两个泛型类型参数Parameter
,类型推断会强制我要么提供两者,要么不提供,这意味着:
public static TReg Parameter<TReg, T>(
this TReg p, string name, T value)
where TReg : ParameterizedRegistrationBase
给了我这个:
Using the generic method 'ConsoleApplication16.Extensions.Parameter<TReg,T>(TReg, string, T)' requires 2 type arguments
Using the generic method 'ConsoleApplication16.Extensions.Parameter<TReg,T>(TReg, string, T)' requires 2 type arguments
这同样糟糕。
我可以轻松地重组类,甚至通过将它们引入层次结构来使方法成为非扩展方法,但我的问题是我是否可以避免为两个后代复制方法,并且以某种方式只声明一次, 对于基类。
让我重新表述一下。有没有办法更改上面第一个代码示例中的类,以便保留 Main-method 中的语法,而无需复制相关方法?
代码必须与 C# 3.0 和 4.0 兼容。
编辑:我不想将两个泛型类型参数都留给推理的原因是,对于某些服务,我想为一种类型的构造函数参数指定一个参数值,但传入一个后代值。目前,指定参数值和要调用的正确构造函数的匹配是使用参数的名称和类型完成的。
让我举个例子吧:
ServiceContainerBuilder.Register<ISomeService>(r => r
.From(f => f.ConcreteType<FileService>(ct => ct
.Parameter<Stream>("source", new FileStream(...)))));
^--+---^ ^---+----^
| |
| +- has to be a descendant of Stream
|
+- has to match constructor of FileService
如果我将两者都留给类型推断,则参数类型将是FileStream
,而不是Stream
。