我正在尝试实现泛型方法,该方法用于将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();
}
}
}
问题:
答案 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);
}
}
对我来说这看起来更简单 - 但它确实意味着你不能以“流利”的方式写它。