Task. Run how to write it with the name action and ref

I stand for a β€œsmall” syntax problem and cannot figure out how to write what I want to.

I have the following method:

public void DoSomeMagic(string foo, ref string bar) { //DoSomeMagic... } 

Now I would like to unload this code inside Task.Run, I would usually write the following:

 public async void Button_Click(object sender, EventArgs args) { string foo = "Hello Foo"; string bar = "Hello Bar"; await Task.Run(() => DoSomeMagic(foo, ref bar)); } 

This does not compile, telling me: "It is not possible to use the" ref "or" out "parameter in the anonymous method panel"

So I thought, why even does () =>, since I just call the method, and I could reduce it to this:

 Task.Run(DoSomeMagic(foo, ref bar)); 

This again does not compile, telling me: the Run (void) method cannot be resolved, the candidates: Run (Action) and Run (Func)

So again, the problem with Visual Studio is my team.

And changed the code to this:

 Task.Run((Action)DoSomeMagic(foo, ref bar)); 

Doesn’t compile again, telling me: cannot express an expression like 'void' to type β€œAction”,

Well, this is starting to get complicated ...

I, than tried, instead of returning void, I just try int and throw it in Func giving me an error: cannot express an expression like "int" to type "Func"

I saw that it would come, but I thought, let's try:

So, I tried the following approach:

 public Action CallDoSomeMagic(string foo, ref string bar) { //DoSomeMagic... return new Action(() => DoSomeMagic(foo, ref bar)); } Task.Run(CallDoSomeMagic); 

But this again gives me the message "Cannot use the" ref "or" out "parameter" bar "inside the body of the anonymous method"

As my headache grows with every attempt, I thought you guys can help me. Is it possible?

+6
source share
1 answer

As the message says: you cannot do this.

You can take a copy of the parameter value and fix it, for example:

 public Action CallDoSomeMagic(string foo, ref string bar) { var snapshot = bar; return new Action(() => DoSomeMagic(foo, ref snapshot)); } 

But note that snapshot updates are not displayed outside the caller through bar .

The reasons for this are twofold:

First, the captured variables used on the lambda become fields in the context class generated by the compiler. This works fine for parameters without ref / out, as they already have copy semantics. So in the case of my snapshot example, this is actually:

 var ctx = new CompilerGeneratedContextClassWithHorribleName(); ctx.foo = foo; ctx.snapshot = bar; return new Action(ctx.CompilerGeneratedMethod); 

where is CompilerGeneratedMethod :

 DoSomeMagic(foo, ref snapshot); 

this is not possible for links, since you essentially need the field to be a link to a link string that is ... messy.

But more importantly, consider the caller's lifetime. It could be:

 void SomeMethod() { string s = "abc"; CallDoSomeMagic("def", ref s); } 

Please note, in particular, that the code should work even if the delegate is called much later - as in the case itself, we can expect it in your case, since it includes Task and async . Now: if SomeMethod came out: where is this link to a line pointer referenced? Hint: this is just an arbitrary place on the stack, now out of scope.


Just to simplify the workaround: instead of passing the ref string bar consider passing SomeType obj , where obj.Bar is the string you need; i.e.

 public Action CallDoSomeMagic(string foo, SomeType obj) { return new Action(() => DoSomeMagic(foo, obj)); } public void DoSomeMagic(string foo, SomeType obj) { // read and write obj.Bar here } 

Note that you could also move foo to obj.Foo if you want.

+10
source

Source: https://habr.com/ru/post/973090/


All Articles