【问题标题】:Reference delegate type引用委托类型
【发布时间】:2018-09-03 18:59:27
【问题描述】:

为什么这个程序使用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 不能使用 ref、out 或 in 参数 'seq' 在一个 匿名方法、lambda 表达式、查询表达式或本地 函数 CsharpRefLambdaTest

问题在于参数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`的定义代码。

【问题讨论】:

  • 你的问题与参考types无关;问题是ref 参数。你不需要那个;不要使用它。
  • @SLaks 这是一个简化的程序。实际上我要在 F 中更改 seq。但是错误阻止了。
  • @nano 如果这是你想要的,最好return组合的 lambda - 即 Sequence F(Sequence seq) { ... }return
  • 您需要将其包装在可以携带它的东西中,然后将其作为属性添加到携带类型中,该类型只是为了在方法调用之间携带值而存在,并允许此属性有一个setter,那么它可以被改变。如错误所示,您不能像这样将ref 与本地函数或委托一起使用。
  • @LasseVågsætherKarlsen 或干脆return it :)

标签: c# lambda delegates


【解决方案1】:

这里的错误信息是:“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,其中包含对堆栈位置的引用(对本地 bar 的引用)。现在请注意,这个 lambda 作为一个对象,可能比 Foo 寿命更长,而 bar 将是一个未定义的 - 并且可能被重用的 - 内存位置。

所以:你不能“捕获”作为refin 我们的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 参数,这意味着:现在我们不可能捕获堆栈位置。

【讨论】:

  • 我需要使用seq的值来定义一个新的Sequenceseq2然后赋值seq = seq2。但是seq的值不可用。
  • @nano 就像我说的:return 它而不是使用ref;或正如其他人所说:使用具有可以放置它的字段或属性的对象。这里的问题是堆栈语义和避免捕获本地引用的可能性
  • 其实我可以使用seq的副本来定义seq2
  • @nano like in bit 在水平线下,然后呢?
  • 我可以在我的第二个代码中删除G 函数并使用seq 的副本来定义seq2。这就是G 所做的。
猜你喜欢
  • 2011-12-15
  • 1970-01-01
  • 1970-01-01
  • 2011-04-09
  • 2023-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-03
相关资源
最近更新 更多