【发布时间】:2010-05-23 22:11:55
【问题描述】:
我正在为我的 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。
【问题讨论】:
标签: c# generics type-inference fluent-interface