Eric Lippert在http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx的博客文章中解释了为什么不考虑类型推断的约束,这是有道理的,因为方法不能通过简单地改变类型约束来重载。但是,我想找到一种使用两种泛型类型实例化对象的方法,一种可以推断出来,另一种可以在考虑约束时推断出来,而不必指定任何类型。
鉴于类型:
public interface I<T>
{
Other<T> CreateOther();
}
public class C : I<string>
{
public Other<string> CreateOther()
{
return new Other<string>();
}
}
public class Other<T>
{
}
和工厂:
public static class Factory1
{
public static Tuple<T, Other<T1>> Create<T, T1>(T o) where T : I<T1>
{
return new Tuple<T, Other<T1>>(o, o.CreateOther());
}
}
以下所需代码无法编译:
public void WontCompile()
{
C c = new C();
var v = Factory1.Create(c); // won't compile
}
错误消息是“错误CS0411:方法'yo.Factory1.Create(T)'的类型参数无法从用法中推断出来。请尝试明确指定类型参数。”,这与Eric所说的一致在他的博客文章中。
因此,我们可以简单地明确指定泛型类型参数,如错误消息所示:
public void SpecifyAllTypes()
{
C c = new C();
var v = Factory1.Create<C, string>(c); // type is Tuple<C, Other<string>>
}
如果我们不希望指定类型参数并且我们不需要保留类型C,我们可以使用以下工厂:
public static class Factory2
{
public static Tuple<I<T1>, Other<T1>> CreateUntyped<T1>(I<T1> o)
{
return new Tuple<I<T1>, Other<T1>>(o, o.CreateOther());
}
}
现在指定:
public void Untyped()
{
C c = new C();
var v = Factory2.CreateUntyped(c); // type is Tuple<I<string>, Other<string>>
}
但是,我希望在返回的对象中保留C类,而不是指定类型。
答案 0 :(得分:5)
我想出了一个解决这个问题的方法,但它似乎是一个解决方法的一部分,其中C类对象在两步工厂调用中使用了两次。
为此,使用以下工厂:
public static class Factory3
{
public static Factory<T1> CreateFactory<T1>(I<T1> o)
{
return new Factory<T1>();
}
}
public class Factory<T1>
{
public Tuple<T, Other<T1>> Create<T>(T o) where T : I<T1>
{
return new Tuple<T, Other<T1>>(o, o.CreateOther());
}
}
然后可以按如下方式使用:
public void Inferred()
{
C c = new C();
var v = Factory3.CreateFactory(c).Create(c); // type is Tuple<C, Other<string>>
}
由于c使用了两次,这感觉很奇怪。第一次使用时,它实际上被丢弃,因为它只是用于推断基类型参数。
对于这个问题,是否有更好的解决方案,其中对象不需要使用两次而且不需要指定类型?
编辑我刚才意识到,虽然该对象必须使用两次,但不需要第二个工厂类。相反,两个参数都可以在如下的相同工厂方法中使用:
public class Factory
{
public Tuple<T, Other<T1>> Create<T, T1>(T o, I<T1> o2) where T : I<T1>
{
return new Tuple<T, Other<T1>>(o, o.CreateOther());
}
}
这将使用如下:
public void Inferred()
{
C c = new C();
var v = Factory.Create(c, c); // type is Tuple<C, Other<string>>
}
它仍然不理想,但比创建第二个工厂类更好,并且至少可以使用XMLDoc注释来指示两个参数应该是同一个对象。同样,一个参数(在本例中为o2
)仅用于推断T
的约束类型。