如何解决泛型方法中类型推断的局限性

时间:2011-08-21 16:31:54

标签: c# generics extension-methods type-inference

我正在尝试实现泛型方法,该方法用于将Tuple<Descendant>类型的对象转换为Tuple<Ancestor>类型的对象。我遇到了一个似乎是C#语言限制的问题。

using System;

namespace Samples
{
    public static class TupleExtensions
    {
        public static Tuple<SuperOfT1> ToSuper<T1, SuperOfT1>(this Tuple<T1> target)
            where T1 : SuperOfT1
        {
            return new Tuple<SuperOfT1>(target.Item1);
        }
    }

    public interface Interface { }

    public class Class : Interface { }

    static class Program
    {
        static void Main()
        {
            var tupleWithClass = new Tuple<Class>(new Class());

            // Next instruction lead the compilation to error. :( The compiler doesn't try to infer types if at least one of generic type arguments is explicitly declared.
            var tupleWithInterfaceIncorrect = tupleWithClass.ToSuper<Interface>();

            // Next instruction is correct. But it looks ugly.
            var tupleWithInterfaceCorrect = tupleWithClass.ToSuper<Class, Interface>();

            // The code I try to write in my software is even worse:
            // I need to declare more types explicitly, because my actual tuple has more dimensions.
            // var myTupleInProduction = tuple.ToSuper<Class1<T>, Class2<T>, Interface1<T>, Interface2<T>>();
            // This code is VB.NET-like (too verbose). The problem is compounded by the fact that such code is used in many places.

            // Can I rewrite my TupleExtensions to provide more laconic code like that:
            // var myTupleInProduction = tuple.ToSuper<Interface1<T>, Interface2<T>>();

            Console.ReadKey();
        }
    }
}

问题:

  1. 您是否知道如何使此代码正常工作(在代码示例中考虑重要注释)?
  2. 如果不是,那么你会如何推荐我一起工作呢?我想保持代码简单明了。

1 个答案:

答案 0 :(得分:3)

假设我已正确阅读您的问题,您想要推断一些类型参数而不是其他参数。这意味着您需要已经处于某种具有可以推断的类型参数的上下文中,因此您可以指定其他上下文。你可以这样做:

using System;

public interface Interface { }

public class Class : Interface { }


public sealed class TupleHelper<T1>
{
    private readonly Tuple<T1> tuple;

    internal TupleHelper(Tuple<T1> tuple)
    {
        this.tuple = tuple;
    }

    // Unfortunately you can't express the constraint the way 
    // round you want here...
    public Tuple<TSuper1> Super<TSuper1>()
    {
        return new Tuple<TSuper1>((TSuper1) (object) tuple.Item1);
    }
}

public static class TupleExtensions
{
    public static TupleHelper<T1> To<T1>(this Tuple<T1> tuple)
    {
        return new TupleHelper<T1>(tuple);
    }
}

class Test
{
    static void Main()
    {

        Tuple<Class> tupleWithClass = new Tuple<Class>(new Class());

        Tuple<Interface> tupleWithInterfaceIncorrect = 
            tupleWithClass.To().Super<Interface>();
    }
}

...但是这并没有给你你想要的约束,因为你不能写where T1 : TSuper1

另一方面,您可以反转操作,如下所示:

using System;
using System.Net;

public interface Interface { }

public class Class : Interface { }

public static class TupleHelper<T1>
{
    public static Tuple<T1> From<TDerived1>(Tuple<TDerived1> tuple)
        where TDerived1 : T1
    {
        return new Tuple<T1>(tuple.Item1);
    }
}

class Test
{
    static void Main()
    {

        Tuple<Class> tupleWithClass = new Tuple<Class>(new Class());

        Tuple<Interface> tupleWithInterfaceIncorrect = 
            TupleHelper<Interface>.From(tupleWithClass);
    }
}

对我来说这看起来更简单 - 但它确实意味着你不能以“流利”的方式写它。