3

In a similar vein to this question, I decided to see if it was possible to pull out parameters from a method into implicitly-typed variables without having to define the types. My ideal solution would look something like this:

var result = LiftOutParam((out p1, out p2, out p3) => {
    return CallMyMethod(i1, i2, out p1, out p2, out p3);
});
var o1 = result.Item1;
var o2 = result.Item2;
var o3 = result.Item3;
var returnValue = result.Item4;

In this case, CallMyMethod has 2 normal input variables, 3 out parameters, and a return value. I want the compiler to automatically determine the types of o1, o2, o3, and returnValue based on the semantics of the delegate passed into LiftOutParam. So I created some helper delegates and methods, for each combination of out parameters. Here's an example for 3 out parameters:

public delegate TReturn Lift<T1, T2, T3, TReturn>(
    out T1 o1, out T2 o2, out T3 o3);

public static Tuple<T1, T2, T3, TReturn> LiftOutParam<T1, T2, T3, TReturn>(
    Lift<T1, T2, T3, TReturn> Lift) {

    T1 o1;
    T2 o2;
    T3 o3;
    TReturn ret = Lift(out o1, out o2, out o3);
    return new Tuple<T1, T2, T3, TReturn>(o1, o2, o3, ret);
}

In theory, the compiler should be able to determine the types T1, T2, T3, and TReturn based on the semantics of the delegate (out p1, out p2, out p3) => { return CallMyMethod(i1, i2, out p1, out p2, out p3); }. However, the above code doesn't compile unless I specify the types of each of the out parameters in the delegate.

Is there a way to do what I'm trying to accomplish? To write a generic helper method that can pull the values of out parameters into implicitly-typed local variables without having to explicitly define the types anywhere?

Community
  • 1
  • 1
mellamokb
  • 56,094
  • 12
  • 110
  • 136
  • `(out p1, out p2, out p3)` isn't a valid lambda signature. At least, not in LINQPad. Without specifying `out`, I get that I need to use the `out` keyword, but when I do specify it, it doesn't recognize the syntax. This makes sense, because where would that `out` parameter go? – Bobson Jan 15 '13 at 20:21
  • @Bobson: Agreed, I was having the same problem. – mellamokb Jan 15 '13 at 20:23
  • AFAIK that is a restriction on the syntax of lambda expressions and anonymous delegates. If you use `out` or `ref`, then the argument type must be specified. – Mike Zboray Jan 15 '13 at 20:24
  • @mikez: Interesting. Do you have a reference for that? – mellamokb Jan 15 '13 at 20:31
  • @mikez If you use an old-style anonymous method, and this is a `ref` parameter (not `out` parameter) that you're not going to use, you can leave out all parameters and the parenthesis as usual. Like so: `DelegateTypeWithRefParameter f = delegate { Console.WriteLine("Cool"); };` – Jeppe Stig Nielsen Jan 15 '13 at 20:51
  • 2
    @mellamokb The controlling section of the C# spec for lambda expressions is 7.15 which clearly specifies that if `ref` or `out` is used then type must be specified for it to be valid. – Mike Zboray Jan 15 '13 at 20:52
  • @JeppeStigNielsen I think that is consistent with what I said, but to clarify if you *use* the `out` or `ref` keywords in the anonymous function signature (either delegate or lambda style) then the type must be specified. Your delegate does not use the `ref` or `out` keyword. The same section I mentioned above (7.15) applies to the delegate style syntax. – Mike Zboray Jan 15 '13 at 21:01
  • @mikez What I said wasn't meant to "correct" you. I agree the C# Specification states all this. Of course, it would be strange if `ref` or `out` (or `params`) was alone in front of the parameter name; that's the type's position. But there **are** diferences, of course. In a lambda, the parameters must always be there, but the type can be omitted in some cases. In an anonymous method, the parameters can be omitted, but if the parameters are there, they must be with their types. – Jeppe Stig Nielsen Jan 15 '13 at 21:27

2 Answers2

2

C# compiler cannot infer generic types from delegates with method passed in. More here C# 3.0 generic type inference - passing a delegate as a function parameter . It doesn't matter if it is normal parameter or ref/out parameter.

Possibly related: C# cannot infer return types of passed delegates/lambdas as explained in Generic methods in .NET cannot have their return types inferred. Why? .

Community
  • 1
  • 1
Euphoric
  • 12,645
  • 1
  • 30
  • 44
  • I know from the example I linked that it is possible for the compiler to infer the return type. However, I think it's the parameters that are the problem - which didn't apply to my linked example. I think it makes sense now. – mellamokb Jan 15 '13 at 20:39
2

There are two issues here:

  1. When a ref or out modifier is needed for one or more of the parameters of a lambda expression, syntax requires that you must specify the type for all of the parameters of the lambda expression. This is a syntax thing and applies no matter if the compiler would have been able to infer the types or not.

  2. There must be a type to infer. If you have a method

    void MyMethod<T>(Action<T> action)
    {
    }
    

    then you can't call this method like this: MyMethod(t => { }); Because in this case there's no way to infer what T is. T must be infered to be one specific type. This could be a type that depends on another generic parameter that is in scope when the MyMethod is called.

Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181