Reputation: 9729
I've got a
Expression<Func<T1, T2>> source
so I can compile it to a Func<T1, T2>
without any problems:
Func<T1, T2> result = source.Compile();
But now I have a special case, where (given T2 is an int) I have to return a Func<T1, int>
. Of course I can't just cast it, but I also can't find another way to convert it.
public Func<T1, int> GetFuncToReturnId()
{
if (source.ReturnType != typeof(int))
{
throw new InvalidOperationException("source must be of return type must be of type int");
}
return // ??? please help !!!
}
I tried to the parts of the Expression or the compiled Func to the constructor of the Func<T1, int>
but that didn't help. What can I do to
Expression<Func<T1, T2>>
to Expression<Func<T1, int>>
Func<T1, T2>
to Func<T1, int>
?Upvotes: 1
Views: 835
Reputation: 660088
It is unclear from your question precisely what the problem you're having is, but I can make a guess that it is that casting a Func<T1, T2>
to Func<T1, int>
gives an invalid cast error.
The reason this is invalid is because C# is conservative about any conversion from a type parameter to any other type. Suppose you have a user-defined conversion from Foo to Bar. If you have a method
Bar M<T>(T t) { return (Bar)t; }
then you might reasonably expect that M<Foo>(new Foo())
would call the user-defined conversion to Bar
. But C# generics are not templates, and do not re-generate the code for each generic instantiation. This sort of conversion is only valid if there is an identity or a reference conversion, and C# is preventing you from making this common mistake.
Moreover, any time you do a type test on a generic, it's no longer "generic". Generic code is supposed to work the same regardless of type arguments, which is why it is called "generic code". It sounds like what you are doing is working against the purpose of generics.
That said, if you are hell bent on doing it, there are a few ways to do a reference conversion between generic types like this:
class C<T1, T2>
{
void M(Func<T1, int> f) {}
// This way is wrong.
void N1(Func<T1, T2> f)
{
if (f is Func<T1, int>)
M((Func<T1, int>)f); // Error, cannot convert
}
// This works.
void N2(Func<T1, T2> f)
{
var fi = f as Func<T1, int>;
if (fi != null)
M(fi);
}
// This also works.
void N3(Func<T1, T2> f)
{
if (f is Func<T1, int>)
M((Func<T1, int>)(object)f);
}
// This works in C# 7; it's a more concise way to combine the previous two
void N4(Func<T1, T2> f)
{
if (f is Func<T1, int> fi)
M(fi);
}
}
Upvotes: 4
Reputation: 197
You can achieve this by using Convert.ChangeType
var compiled = source.Compile();
return (T1 x) => (int) Convert.ChangeType(compiled(x), typeof(int));
or simply casting twice
var compiled = source.Compile();
return (T1 x) => (int) (object) compiled(x);
Upvotes: 1