Hinek
Hinek

Reputation: 9729

How to convert or cast a Func<T1,T2> to a Func<T1,int>

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

Upvotes: 1

Views: 835

Answers (2)

Eric Lippert
Eric Lippert

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

Taras Shcherban
Taras Shcherban

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

Related Questions