How to work-around the limitations of the type inference in generic methods
I'm trying to implement the generic method which is intended for converting objects of Tuple<Descendant>
type to objects of Tuple<Ancestor>
type. I've stuck with a problem which seems to be a limitation of the C# language.
using System;
namespace Samples
{
public static class TupleExtensions
{
public static Tuple<SuperOfT1> T开发者_Go百科oSuper<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();
}
}
}
Questions:
- Do you have any idea how to make this code working (take in account important comment in the code sample)?
- If no, then how would you recommend me to work it around? I want to keep the code simple and clear.
Assuming I've read your question correctly, you want to infer some type parameters but not others. That means you'll need to already be in some sort of context which has the type parameters that can be inferred, so you can specify the others. You can do it like this:
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>();
}
}
... but that doesn't give you the constraint you want, because you can't write where T1 : TSuper1
.
On the other hand, you could invert the operation, like this:
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);
}
}
This looks simpler to me - but it does mean you can't write it in a "fluent" way.
精彩评论