Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Cannot use ref or out parameter inside an anonymous method body

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);     } } 

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?

like image 689
Dax Fohl Avatar asked Nov 21 '10 02:11

Dax Fohl


1 Answers

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);     } 

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.

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

(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.)

like image 75
Dax Fohl Avatar answered Oct 04 '22 00:10

Dax Fohl