C#不能在匿名方法主体中使用ref或out参数 [英] C# Cannot use ref or out parameter inside an anonymous method body

查看:694
本文介绍了C#不能在匿名方法主体中使用ref或out参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个函数,该函数可以创建一个可以递增传入的整数的Action。但是,我的第一次尝试是给我一个错误无法在匿名方法主体中使用ref或out参数。

I'm trying to create a function that can create an Action that increments whatever integer is passed in. However my first attempt is giving me an error "cannot use ref or out parameter inside an anonymous method body".

public static class IntEx {
    public static Action CreateIncrementer(ref int reference) {
        return () => {
            reference += 1;
        };
    }
}

我知道编译器为什么不喜欢这样,但是尽管如此,我还是希望有一个优美的方法来提供一个可以指向任何整数的好的增量器工厂。我看到的唯一方法是如下所示:

I understand why the compiler doesn't like this, but nonetheless I'd like to have a graceful way to provide a nice incrementer factory that can point to any integer. The only way I'm seeing to do this is something like the following:

public static class IntEx {
    public static Action CreateIncrementer(Func<int> getter, Action<int> setter) {
        return () => setter(getter() + 1);
    }
}

当然,这对于呼叫者使用;要求调用者创建两个lambda,而不仅仅是传递引用。有没有其他更优雅的方式来提供此功能,还是我只需要使用two-lambda选项?

But of course that is more of a pain for the caller to use; requiring the caller to create two lambdas instead of just passing in a reference. Is there any more graceful way of providing this functionality, or will I just have to live with the two-lambda option?

推荐答案

好的,我发现如果在不安全的上下文中使用指针实际上 是可能的:

Okay, I've found that it actually is possible with pointers if in unsafe context:

public static class IntEx {
    unsafe public static Action CreateIncrementer(int* reference) {
        return () => {
            *reference += 1;
        };
    }
}

但是,垃圾收集器对此造成了严重破坏在垃圾回收期间移动引用,如下所示:

However, the garbage collector can wreak havoc with this by moving your reference during garbage collection, as the following indicates:

class Program {
    static void Main() {
        new Program().Run();
        Console.ReadLine();
    }

    int _i = 0;
    public unsafe void Run() {
        Action incr;
        fixed (int* p_i = &_i) {
            incr = IntEx.CreateIncrementer(p_i);
        }
        incr();
        Console.WriteLine(_i); // Yay, incremented to 1!
        GC.Collect();
        incr();
        Console.WriteLine(_i); // Uh-oh, still 1!
    }
}

可以通过将变量固定为内存中的特定位置。可以通过向构造函数添加以下内容来完成此操作:

One can get around this problem by pinning the variable to a specific spot in memory. This can be done by adding the following to the constructor:

    public Program() {
        GCHandle.Alloc(_i, GCHandleType.Pinned);
    }

这使垃圾收集器无法移动对象,所以我们正是重新寻找。但是,您必须添加一个析构函数以释放该销,并且销毁整个对象生命周期中的内存。真的没有那么容易。这在C ++中更有意义,因为C ++中的东西不会四处移动,资源管理是必不可少的,但是在C#中一切都应该是自动的,就没有那么多了。

That keeps the garbage collector from moving the object around, so exactly what we're looking for. However then you've got to add a destructor to release the pin, and it fragments the memory throughout the lifetime of the object. Not really any easier. This would make more sense in C++, where stuff doesn't get moved around, and resource management is par the course, but not so much in C# where all that is supposed to be automatic.

所以看起来这故事的寓意是,只需将该成员int包裹在引用类型中并对其进行处理即可。

So looks like the moral of the story is, just wrap that member int in a reference type and be done with it.

(是的,这是我在问这个问题之前的工作方式,但是只是想弄清楚是否有一种方法可以摆脱我所有的问题引用< int>成员变量,只需使用常规整数即可。哦

(And yes, that's the way I had it working before asking the question, but was just trying to figure out if there was a way I could get rid of all my Reference<int> member variables and just use regular ints. Oh well.)

这篇关于C#不能在匿名方法主体中使用ref或out参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆