【问题标题】:Is there a workaround to C# not being able to infer generic type arguments using type constraints?是否有 C# 无法使用类型约束推断泛型类型参数的解决方法?
【发布时间】:2012-12-22 15:25:39
【问题描述】:

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
    }

错误信息是“error CS0411: The type arguments for method 'yo.Factory1.Create(T)' cannot be inferred from the usage. Try specify the type arguments explicit.”,这与 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 而不是指定类型。

【问题讨论】:

    标签: c# generics type-inference


    【解决方案1】:

    我想出了一个解决这个问题的方法,但它似乎是一种变通方法的组合,其中 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 cmets 来指示两个参数应该是同一个对象。再一次,一个参数(在这种情况下为o2)仅用于推断T 的约束类型。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多