user5540335
user5540335

Reputation:

Reference delegate type

Why does this program show an error for using seq:

class Program
{
    delegate double Sequence(int r);

    void F(ref Sequence seq) // Here
    {
        Sequence seq2 = r =>
        {
            if (r % 2 == 0)
                return seq(r); // Here
            else
                return seq(2 * r); // Here
        };
        seq = seq2;
    }

    static void Main()
    {
    }
}

Error CS1628 Cannot use ref, out, or in parameter 'seq' inside an anonymous method, lambda expression, query expression, or local function CsharpRefLambdaTest

The problem is with that the parameter seq is a reference type. But why is it wrong? what't the problem with a reference seq? If seq is not reference the program has no errors.

Is there any way to correct the program while keeping seq as a reference?

The program is just an test and it is not going to do anything.

================

I need to use the value of seq to define a new Sequence seq2 and then assign seq = seq2. But the values of seq are not usable. If the values of seq are not going to be usable why does C# allow seq to be a reference at all?

===============================

Edit:

The program above is just simplified version of the following:

class Program
{
    delegate double Sequence(int r);

    Sequence G(Sequence seq)
    {
        Sequence seq2 = r =>
        {
            if (r % 2 == 0)
                return seq(r);
            else
                return seq(2 * r);
        };
        return seq2;
    }

    void F(ref Sequence seq)
    {
        seq = G(seq);
    }

    static void Main()
    {
    }
}

But I don't understand why I cannot remove G and instead add the defining code of G insideF`.

Upvotes: 0

Views: 305

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1062512

the error message here is: "CS1628 Cannot use ref, out, or in parameter 'seq' inside an anonymous method, lambda expression, query expression, or local function" - seq2 is the lambda expression; it has nothing to do with reference types, but rather: lifetimes. You could, after all, call it like:

void Foo() {
    Sequence bar = SomeMethod; // bar is a LOCAL of Foo
    F(ref bar);
    // not shown: perhaps do something with bar, perhaps not
}

at which point, F would need to somehow create a lambda that contains within it a reference to a position on the stack (a reference to the local bar). Now note that this lambda, being an object, could outlive Foo, and bar would be an undefined - and possibly reused - memory location.

So: you can't "capture" parameters that are passed as ref, in our out, where I'm using "capture" loosely here to mean "use within the scope of a lambda or anonymous method that forms an expression-tree, delegate expression; or within an iterator block or async continuation".

Just remove the ref. You don't need it, and it isn't helping. If your intention is to change the delegate, then consider instead returning the composed delegate.


as an alternative workaround: snapshot the value and capture the snapshot:

void F(ref Sequence seq)
{
    var tmp = seq;
    seq = r =>
    {
        if (r % 2 == 0)
            return tmp(r);
        else
            return tmp(2 * r);
    };
}

this avoids the problematic scenario because the snapshot dereferences the ref parameter, meaning: there is now no possibility that we're capturing a stack location.

Upvotes: 4

Related Questions