Reputation: 539
I'm using a library with (hundreds of, template-generated) classes without generic modifier, each extending the same classes with generic modifier (essentially to shorten the notation). E.g.
class DArr : NumberObject<double, MyArrayIndexer> { ... }
class IArr : NumberObject<int, MyArrayIndexer> { ... }
class DMat : NumberObject<double, MyMatrixIndexer> { ... }
class IMat : NumberObject<int, MyMatrixIndexer> { ... }
class BMat : NumberObject<bool, MyMatrixIndexer> { ... }
and so on
I now want to write functions that proccess these with a function that requires their internal type (e.g. internal copies that need to know whether we have 4 or 8 bytes per element, and what type of Indexer is being used). I therefore made the signature of my function:
public SomeUnrelatedClass<T> Process<T,TVal,T0>(SomeUnrelatedClass<T> obj) where T : NumberObject<TVal,T0> {
//here some tstuff that requires T, TVal and T0
}
but unfortunately it seems I can't use it in the way I need to, i.e.
SomeUnrelatedClass<DArr> input = ...;
SomeUnrelatedClass<DArr> output = Process(input);
since it fails to derive TVal and T0 from (why??), despite it being uniquely determined. How can I remedy this? Calling it as SomeUnrelatedClass<DArr> output = Process<DArr,double,MyArrayIndexer>(input);
is not an option because in reality there's a lot more of these generics and they're a lot longer in name (and this function will be used thousands of times in future code, so I'd much rather now write a more complicated function that does it right and keeps the syntax simple). Ideally I'd not do a long list of "if type == ..." though, because the list of types is template-generated and changes over time as types get added (meaning this template would be dependent on the other template, etc.)
I've thought of silly hacks like making the syntax Process<T,TVal,T0>(SomeUnrelatedClass<T> obj,SomeUnrelatedClass<NumberObject<TVal,T0>> sameObj)
and calling SomeUnrelatedClass<DArr> output = Process(input,input);
but I feel like that's just a really poor fix. What's a more 'proper' way to do this?
Upvotes: 2
Views: 82
Reputation: 574
I think you might be in some luck here, because C# does not infer types based on the constraints that you specify. I think it's part of the specification, from what I could dig out. But you might be able to solve you problem anyway :) - I managed to get the following running.
{
var input = new DArr(12.34);
var output = Visitor.Process(input);
Console.WriteLine($"Type [{output.Obj.GetType()}] with value {output.Obj.MyType}");
}
{
var input = new IArr(42);
var output = Visitor.Process(input);
Console.WriteLine($"Type [{output.Obj.GetType()}] with value {output.Obj.MyType}");
}
// Generated output:
//
// Type [DArr] with value 12,34
// Type [IArr] with value 42
So no matter how many types you define that Process method will consume and map the input to the right concrete type.
Below is how this could work:
public class NumberObject<TType, TIndexer>
{
public NumberObject(TType type) { MyType = type; }
public TType MyType { get; }
}
public class MyArrayIndexer { }
public class DArr : NumberObject<double, MyArrayIndexer>
{
public DArr(double value) : base(value) { }
}
public class IArr : NumberObject<int, MyArrayIndexer>
{
public IArr(int value) : base(value) { }
}
public class SomeUnrelatedClass<T>
{
public T Obj { get; }
public SomeUnrelatedClass(T obj){ Obj = obj; }
}
public class Visitor
{
public static SomeUnrelatedClass<NumberObject<TVal, T0>> Process<TVal, T0>(NumberObject<TVal, T0> input)
{
return new SomeUnrelatedClass<NumberObject<TVal, T0>>(input);
}
}
The trick here is to reduce the number of generic parameters - getting rid of the T type as it can directly be expressed by the remaining two as you actually also write yourself
T is NumberObject<TVal, T0>
When the level of types to infer is reduced the C# compiler can determine them directly and viola you can leave you specifying generic parameters to the Process method.
Upvotes: 1