首页 > 解决方案 > 引用委托类型

问题描述

为什么这个程序使用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()
    {
    }
}

错误 CS1628 无法在匿名方法、lambda 表达式、查询表达式或本地函数 CsharpRefLambdaTest 内使用 ref、out 或 in 参数“seq”

问题在于参数seq是引用类型。但是为什么错了?参考有什么问题seq?如果seq不是引用,则程序没有错误。

有没有办法在保持 seq 作为参考的同时更正程序?

该程序只是一个测试,它不会做任何事情。

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

我需要使用 的值seq来定义一个新的序列seq2,然后赋值seq = seq2。但是 seq 的值是不可用的。如果 的值seq不可用,为什么 C# 允许seq作为参考?

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

编辑:

上面的程序只是以下程序的简化版本:

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()
    {
    }
}

但我不明白为什么我不能删除G而是添加G insideF` 的定义代码。

标签: c#lambdadelegates

解决方案


这里的错误消息是:“CS1628 不能在匿名方法、lambda 表达式、查询表达式或本地函数中使用 ref、out 或 in 参数 'seq'” -seq2是 lambda 表达式;它与引用类型无关,而是:生命周期。毕竟,你可以这样称呼它:

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

此时,F需要以某种方式创建一个 lambda,其中包含对堆栈上位置的引用(对 local 的引用bar)。现在请注意,这个 lambda 作为一个对象,可能比它的寿命更长Foo,并且bar可能是一个未定义的 - 并且可能被重用的 - 内存位置。

所以:你不能“捕获”作为ref, inour传递的参数,out我在这里松散地使用“捕获”来表示“在形成表达式树、委托表达式的 lambda 或匿名方法的范围内使用;或在迭代器块或异步延续中”。

只需删除ref. 你不需要它,它没有帮助。如果您的意图是更改委托,则考虑改为返回组合委托。


作为替代解决方法:对值进行快照并捕获快照:

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

这避免了有问题的场景,因为快照取消了对参数的引用ref,这意味着:现在我们不可能捕获堆栈位置。


推荐阅读