I know that what I'm doing can be done in a different way, but I'm curious about how things work. The following is a simplified code which doesn't compile, but it supposed to show my goal.
private void Execute()
{
GeneralizedFunction("1", "2", i => Transform(i));
}
void GeneralizedFunction(string aStringA, string aStringB, Func<string, T> aAction)
{
A result1 = aAction(aStringA);
B result2 = aAction(aStringB);
// Do something with A and B here
}
T Transform<T>(string aString)
{
return default(T);
}
Transform
is a generic convertion from string to some object (think deserialization).
GeneralizedFunction
uses two specializations of transform: one for type A and one for type B. I know I can do this in a number of other ways (say by introducing a parameter for the type of the object), but I'm looking for explanations of whether it is possible or impossible to do this with generics/lambdas. If Transform is specialized before it is passed as a parameter to GeneralizedFunction, then it's impossible. Then the question is why this possibility is restricted.
This answer doesn't explain the reason why, just how to work around the limitation.
Instead of passing an actual function, you can pass an object that has such a function:
interface IGenericFunc
{
TResult Call<TArg,TResult>(TArg arg);
}
// ... in some class:
void Test(IGenericFunc genericFunc)
{
// for example's sake only:
int x = genericFunc.Call<String, int>("string");
object y = genericFunc.Call<double, object>(2.3);
}
For your specific use case, it can be simplified to:
interface IDeserializerFunc
{
T Call<T>(string arg);
}
// ... in some class:
void Test(IDeserializerFunc deserializer)
{
int x = deserializer.Call<int>("3");
double y = deserializer.Call<double>("3.2");
}
What you're asking to do isn't possible using generics alone. The compiler needs to generate two typed versions of your Transform
function: one to return type A
and one for type B
. The compiler has no way of knowing to generate this at compile time; only by running the code would it know that A and B are required.
One way to solve it would be to pass in the two versions:
private void Execute()
{
GeneralizedFunction("1", "2", i => Transform<A>(i), i => Transform<B>(i));
}
void GeneralizedFunction(string aStringA, string aStringB, Func<string, A> aAction, Func<string, B> bAction)
{
A result1 = aAction(aStringA);
B result2 = bAction(aStringB);
}
The compiler knows exactly what it needs to generate in this case.
Try the following signature:
void GeneralizedFunction<T>(string aStringA, string aStringB, Func<string, T> aAction)
(Note that GeneralizedFunction has to be generic; the compiler will automatically guess the type parameter when calling the method).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With