是否有一种解决方法使C#无法使用类型约束推断泛型类型参数?

jam*_*eff 10 c# generics type-inference

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>
{
}
Run Code Online (Sandbox Code Playgroud)

和工厂:

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

以下所需的代码将无法编译:

    public void WontCompile()
    {
        C c = new C();
        var v = Factory1.Create(c); // won't compile
    }
Run Code Online (Sandbox Code Playgroud)

错误消息是"错误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>>
    }
Run Code Online (Sandbox Code Playgroud)

如果我们不希望指定类型参数,并且我们不需要保留类型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());
    }
}
Run Code Online (Sandbox Code Playgroud)

现在指定:

    public void Untyped()
    {
        C c = new C();
        var v = Factory2.CreateUntyped(c); // type is Tuple<I<string>, Other<string>>
    }
Run Code Online (Sandbox Code Playgroud)

但是,我希望在返回的对象中保留类型C,而不是指定类型.

jam*_*eff 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());
    }
}
Run Code Online (Sandbox Code Playgroud)

然后可以按如下方式使用:

    public void Inferred()
    {
        C c = new C();
        var v = Factory3.CreateFactory(c).Create(c); // type is Tuple<C, Other<string>>
    }
Run Code Online (Sandbox Code Playgroud)

这只是感觉很奇怪,因为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());
    }
}
Run Code Online (Sandbox Code Playgroud)

这将使用如下:

public void Inferred()
{
    C c = new C();
    var v = Factory.Create(c, c); // type is Tuple<C, Other<string>>
}
Run Code Online (Sandbox Code Playgroud)

它仍然不理想,但比创建第二个工厂类更好,并且至少XMLDoc注释可用于指示两个参数应该是同一个对象.再一次,一个参数(o2在这种情况下)仅用于推断约束类型T.