C#中可能存在部分泛型类型推断?
我正在为我的IoC类库重写我的fluent接口,当我重构一些代码以便通过基类共享一些公共功能时,我遇到了一个障碍 注意:这是我想做的事,而不是我必须做的事。如果我必须使用不同的语法,我会的,但是如果有人知道如何让我的代码按照我想要的方式编译,我会非常欢迎 我希望一些扩展方法可以用于特定的基类,这些方法应该是泛型的,有一个泛型类型,与方法的参数相关,但是这些方法还应该返回与调用它们的特定后代相关的特定类型 我认为使用代码示例比上面的描述更好 下面是一个简单而完整的例子,说明什么是不起作用的: 我想要的是扩展方法(C#中可能存在部分泛型类型推断?,c#,generics,type-inference,fluent-interface,C#,Generics,Type Inference,Fluent Interface,我正在为我的IoC类库重写我的fluent接口,当我重构一些代码以便通过基类共享一些公共功能时,我遇到了一个障碍 注意:这是我想做的事,而不是我必须做的事。如果我必须使用不同的语法,我会的,但是如果有人知道如何让我的代码按照我想要的方式编译,我会非常欢迎 我希望一些扩展方法可以用于特定的基类,这些方法应该是泛型的,有一个泛型类型,与方法的参数相关,但是这些方法还应该返回与调用它们的特定后代相关的特定类型 我认为使用代码示例比上面的描述更好 下面是一个简单而完整的例子,说明什么是不起作用的: 我想
参数
)能够在ConcreteTypeRegistration
和DelegateRegistration
上调用,并且在这两种情况下,返回类型应该与调用扩展的类型匹配
问题如下:
我想写:
ct.Parameter<string>("name", "Lasse")
^------^
notice only one generic argument
给我这个:
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
使用泛型方法“ConsoleApplication16.Extensions.Parameter(TReg,string,T)”需要2个类型参数
使用泛型方法“ConsoleApplication16.Extensions.Parameter(TReg,string,T)”需要2个类型参数
这同样糟糕
我可以很容易地重新构造类,甚至通过将方法引入层次结构使方法成为非扩展方法,但我的问题是,我是否可以避免为两个子体复制方法,并且以某种方式为基类只声明一次方法
让我换一种说法。有没有办法更改上面第一个代码示例中的类,这样就可以保留Main方法中的语法,而不必重复所讨论的方法
代码必须与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
ServiceContainerBuilder.Register(r=>r
.From(f=>f.ConcreteType(ct=>ct
.Parameter(“源”,新的文件流(…));
^--+---^ ^---+----^
| |
|+-必须是流的后代
|
+-必须匹配FileService的构造函数
如果将两者都留给类型推断,那么参数类型将是
FileStream
,而不是Stream
为什么不指定零类型参数?两者都可以在您的样本中推断出来。如果这对您来说不是一个可接受的解决方案,那么我也经常遇到这个问题,并且没有简单的方法来解决“仅推断一个类型参数”的问题。因此,我将使用复制方法。如果您只有两种特定类型的注册(您的问题似乎就是这样),那么您可以简单地实现两种扩展方法:
public static DelegateRegistration Parameter<T>(
this DelegateRegistration p, string name, T value);
public static ConcreteTypeRegistration Parameter<T>(
this ConcreteTypeRegistration p, string name, T value);
公共静态委托注册参数(
该委托人(p、字符串名称、T值);
公共静态类型注册参数(
该值为(p,字符串名称,T值);
那么您就不需要指定类型参数,因此类型推断可以在您提到的示例中工作。请注意,您可以通过委托给一个具有两个类型参数(您问题中的那个)的泛型扩展方法来实现这两个扩展方法
一般来说,C#不支持像
o.Foo(..)
这样的东西来推断第二个类型参数(这将是一个很好的特性-F#拥有它并且它非常有用:-))。您可能会实现一种变通方法,允许您编写此代码(基本上,将调用分为两个方法调用,以获得两个可以应用类型推断的位置):
FooTrick().Apply();//其中Apply是一个泛型方法
下面是一个伪代码来演示该结构:
// in the original object
FooImmediateWrapper<T> FooTrick<T>() {
return new FooImmediateWrapper<T> { InvokeOn = this; }
}
// in the FooImmediateWrapper<T> class
(...) Apply<R>(arguments) {
this.InvokeOn.Foo<T, R>(arguments);
}
//在原始对象中
FooImmediateWrapper FooTrick(){
返回新的FooImmediateWrapper{InvokeOn=this;}
}
//在FooImmediateWrapper类中
(…)应用(参数){
this.InvokeOn.Foo(参数);
}
以下内容如何:
使用您提供的定义:
公共静态TReg参数(
这个树(p,字符串名,T值)
其中TReg:ParameterizedRegistrationBase
然后强制转换参数,使推理机获得正确的类型:
ServiceContainerBuilder.Register<ISomeService>(r => r
.From(f => f.ConcreteType<FileService>(ct => ct
.Parameter("source", (Stream)new FileStream(...)))));
ServiceContainerBuilder.Register(r=>r
.From(f=>f.ConcreteType(ct=>ct
.Parameter(“源”,(流)新文件流(…));
我认为您需要在两个不同的表达式之间拆分两个类型参数;使显式方法成为扩展方法的参数类型的一部分,这样推理就可以提取它
假设您声明了一个包装器类:
public class TypedValue<TValue>
{
public TypedValue(TValue value)
{
Value = value;
}
public TValue Value { get; private set; }
}
但在需要显式声明类型的情况下,可以这样做:
ct.Parameter("list", new TypedValue<IEnumerable<int>>(new List<int>()))
ct.参数(“列表”,新类型值(新列表())
看起来很难看,但希望比简单的完全推断的那种更稀有
请注意,您可以使用no-wrapper重载并写入:
ct.Parameter("list", (IEnumerable<int>)(new List<int>()))
ct.Parameter(“list”,(IEnumerable)(new list())
但是,这当然有一个缺点,就是如果出现错误,在运行时就会失败。不幸的是,我现在离开了我的C#编译器,如果这太远了,我很抱歉。我想创建一个ext
// in the original object
FooImmediateWrapper<T> FooTrick<T>() {
return new FooImmediateWrapper<T> { InvokeOn = this; }
}
// in the FooImmediateWrapper<T> class
(...) Apply<R>(arguments) {
this.InvokeOn.Foo<T, R>(arguments);
}
ServiceContainerBuilder.Register<ISomeService>(r => r
.From(f => f.ConcreteType<FileService>(ct => ct
.Parameter("source", (Stream)new FileStream(...)))));
public class TypedValue<TValue>
{
public TypedValue(TValue value)
{
Value = value;
}
public TValue Value { get; private set; }
}
public static class Extensions
{
public static TReg Parameter<TValue, TReg>(
this TReg p, string name, TypedValue<TValue> value)
where TReg : ParameterizedRegistrationBase
{
// can get at value.Value
return p;
}
}
public static class Extensions
{
public static TReg Parameter<TValue, TReg>(
this TReg p, string name, TValue value)
where TReg : ParameterizedRegistrationBase
{
return p;
}
}
ct.Parameter("name", "Lasse")
ct.Parameter("list", new TypedValue<IEnumerable<int>>(new List<int>()))
ct.Parameter("list", (IEnumerable<int>)(new List<int>()))
listOfFruits.ThatAre<Banana>().Where(banana => banana.Peel != Color.Black) ...
public static IEnumerable<TResult> ThatAre<TSource, TResult>
(this IEnumerable<TSource> source) where TResult : TSource
public static ThatAreWrapper<TSource> That<TSource>
(this IEnumerable<TSource> source)
{
return new ThatAreWrapper<TSource>(source);
}
public class ThatAreWrapper<TSource>
{
private readonly IEnumerable<TSource> SourceCollection;
public ThatAreWrapper(IEnumerable<TSource> source)
{
SourceCollection = source;
}
public IEnumerable<TResult> Are<TResult>() where TResult : TSource
{
foreach (var sourceItem in SourceCollection)
if (sourceItem is TResult) yield return (TResult)sourceItem;
}
}
}
listOfFruits.That().Are<Banana>().Where(banana => banana.Peel != Color.Black) ...
listOfFruits.That().Are<Truck>().Where(truck => truck.Horn.IsBroken) ...
listOfFruits.OfType<Truck>().Where(truck => truck.Horn.IsBroken) ...