15

This is not a dupe of Calling a method with ref or out parameters from an anonymous method

I am wondering why out parameters are not allowed within anonymous methods. Not allowing ref parameters makes a bit more sense to me, but the out parameters, not as much.

what are your thoughts on this

Community
  • 1
  • 1
Chris Ballance
  • 33,810
  • 26
  • 104
  • 151
  • I'm wondering WHY you'd ever pass an anonymous type outside of a method body, unless you're wanting to learn the joy that is Reflection. –  Oct 28 '09 at 15:06
  • Does this answer on the original link question not answer this sufficiently? http://stackoverflow.com/questions/801147/scope-of-anonymous-methods/801563#801563 – Binary Worrier Oct 28 '09 at 15:09
  • 1
    @Will: This has nothing to do with anonymous types. – SLaks Oct 28 '09 at 15:16
  • 2
    I misunderstood just how similar ref and out actually are to the CLR, thanks for the comments. – Chris Ballance Oct 28 '09 at 15:19
  • possible duplicate of [cannot-use-ref-or-out-parameter-in-lambda-expressions](http://stackoverflow.com/questions/1365689/cannot-use-ref-or-out-parameter-in-lambda-expressions) – nawfal Dec 06 '13 at 09:10

4 Answers4

31

In some ways this is a dupe. Out parameters are ref parameters. There is simply an extra attribute on the value that is used by the C# language. The reason for disallowing them is the exact same as ref parameters.

The problem here originates with the effect of using a value declared outside the anonymous method within the anonymous method. Doing so will capture the value within the lambda and out of necessity arbitrarily extend its lifetime beyond that of the current function. This is not compatible with out parameters which have a fixed lifetime.

Imagine for instance that the out parameter referred to a local variable on the stack. The lambda can execute at any arbitrary point in the future and hence could execute when that stack frame was no longer valid. What would the out parameter mean then?

George Stocker
  • 57,289
  • 29
  • 176
  • 237
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 1
    +1 Had started typing an example similar to your last paragraph. Thanks for saving me the bother :) – Binary Worrier Oct 28 '09 at 15:12
  • Second paragraph turned the lightbulb on. Thanks @JaredPar! – Chris Ballance Oct 28 '09 at 15:20
  • But wouldn't that apply to any other kind of variable? I mean, what if I use a variable inside a lambda expression which is located in the stack, just access some field. If the lambda expressión executes when the stack is no longer valid, Wouldn't the object have already been collected? If it isn't, when would the garbage collector collect that object? – Jorge Córdoba Oct 28 '09 at 15:40
  • 2
    @Jorge, if you use a local variable within a lambda expression it will not ever be located on the stack. The closure capturing mechanism will actually remove it entirely from the method (check the output with reflector). Parameters are a little bit different in that they are just copied into the lambda at the start of the method. – JaredPar Oct 28 '09 at 15:43
  • 3
    One minor nit pick: the "outness" of a ref argument is not represented by a modopt, it's represented by an attribute. It might have been a better idea to use a modopt because then you could have two methods of the same signature that differed solely in out/ref-ness. But attributes are not considered when evaluating signature equality. – Eric Lippert Nov 06 '09 at 07:14
  • Could you expand on reference types? This is my understanding, but I'm not sure if it's correct. `ref IEnumerable numbers` cannot be used in a lambda because this *reference to a reference* is on the stack and thus has the same limitation, but `var numbersCopy = numbers;` inside the method can then be used in a lambda because this is a new *reference to a reference* (so a new entity on the stack). [full snippet in case my question is unclear](http://pastebin.com/0TDWxHnL) – user247702 Feb 10 '14 at 14:09
6

This is basically to do with the fact that parameters of an anonymous delegate/lambda expressions are captured variables, and capturing ref/out variables doesn't make any sense in C#/the CLR, since it would require ref/out fields internally. Also, note that I pair both these keywords because they are effectively the same.

If you want a complete explanation, Eric Lippert discussed this design point in detail on his blog. (See the paragraphs near the bottom in particular.)

Chris Ballance
  • 33,810
  • 26
  • 104
  • 151
Noldorin
  • 144,213
  • 56
  • 264
  • 302
1

The only difference between out and ref parameters is that an out parameter will have an [out] token applied to it. They're the same thing as far as the CLR is concerned.

In order to implement it, the compiler would have to generate ref fields, which are not supported.

If you think about it, you'll realize that it makes no sense to allow an anonymous method to use an out parameter.

What would the following code to?

static Func<object, object> Mess(out object param) {
    param = "Original";
    return i => param = i;
}
static Func<object, object> MessCaller() {
    object local;
    return Mess(out local);
}
static vouid Main() {
    Console.WriteLine(MessCaller()("New"));
    //The local variable that the lambda expression writes to doesn't exist anymore.
}
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • 1
    `OutAttribute` attribute is not the thing applied to `out` parameters in C#. `OutAttribute` is a custom attribute considered when marshaling stuff. `out` parameters are annotated by another metadata token (`[out] in IL`). You can verify this fact by querying the attributes of a `ParameterInfo` declared as `out` in C# code. There's no `[OutAttribute]` there. – Mehrdad Afshari Oct 28 '09 at 15:24
1

I came across this conundrum whilst developing some error handling code. I wanted to pass a reference (out) to an error message that would get logged. This gave my anonymous methods a chance to perform multiple checks, each setting the error message as necessary.

I ended up writing a new wrapper for the anonymous method that worked differently. But what I thought might be of some value to someone, is that I could have simply made a private method that had an out parameter, and defined a delegate, and made my code use that. Hope this helps / inspires somebody.

    protected delegate void OutStringDelegate(int divider, out string errorText);
    protected void codeWrapper(int divider, OutStringDelegate del)
    {
        string ErrorMessage = "An Error Occurred.";

        try
        {
            del(divider, out ErrorMessage);
        }
        catch
        {
            LogError(ErrorMessage);
        }
    }
    public void UseWrapper(int input)
    {
        codeWrapper(input, codeToCall);
    }
    private int somePrivateValue = 0;
    private void codeToCall(int divider, out string errorMessage)
    {
        errorMessage = "Nice Error Message here!";
        somePrivateValue = 1 / divider; // call me with zero to cause error.
    }
    private void LogError(string msg)
    {
        Console.WriteLine(msg);
    }