prependAction = (x => prefi...">

Prevent .NET from "Canceling" Local Variables

I have the following code:

string prefix = "OLD:"; Func<string, string> prependAction = (x => prefix + x); prefix = "NEW:"; Console.WriteLine(prependAction("brownie")); 

Since the compiler replaces the prefix variable with closure, "NEW: brownie" is printed to the console.

Is there an easy way to stop the compiler from removing the prefix variable while continuing to use the lambda expression? I would like my Func to work identically:

 Func<string, string> prependAction = (x => "OLD:" + x); 

The reason I need this is to serialize the resulting delegate. If the prefix variable is in a non-serializable class, the above function will not be serialized.

The only way I see at the moment is to create a new serializable class that saves the string as a member variable and has a string prefix method:

 string prefix = "NEW:"; var prepender = new Prepender {Prefix = prefix}; Func<string, string> prependAction = prepender.Prepend; prefix = "OLD:"; Console.WriteLine(prependAction("brownie")); 

Using the helper class:

 [Serializable] public class Prepender { public string Prefix { get; set; } public string Prepend(string str) { return Prefix + str; } } 

It seems like a lot of work to make the compiler be "stupid".

+7
c # lambda
source share
9 answers

Now I see the main problem. It is deeper than I thought. Basically, the solution is to modify the expression tree before it is serialized, replacing all subtrees that are independent of parameters with constant nodes. This is apparently called "funcletization." There is an explanation for this here .

+8
source share

Just do another close ...

Say something like:

 var prepend = "OLD:"; Func<string, Func<string, string>> makePrepender = x => y => (x + y); Func<string, string> oldPrepend = makePrepender(prepend); prepend = "NEW:"; Console.WriteLine(oldPrepend("Brownie")); 

I have not tested it yet, since I do not have access to VS at the moment, but usually I solve this problem this way.

+2
source share

Lambdas automatically "suck" in local variables, I'm afraid it's just how they work by definition.

+1
source share

This is a fairly common problem, that is, variables that were inadvertently changed by closure - a much simpler solution - is simple:

 string prefix = "OLD:"; var actionPrefix = prefix; Func<string, string> prependAction = (x => actionPrefix + x); prefix = "NEW:"; Console.WriteLine(prependAction("brownie")); 

If you use resharper, it actually identifies places in your code where you run the risk of unexpected side effects such as this - so if the file is green, your code should be fine.

I think that in a sense it would be nice if we had some kind of syntactic sugar to handle this situation, so we could write it as a one-line i.e.

 Func<string, string> prependAction = (x => ~prefix + x); 

If any prefix operator causes the calculation of the value of the variable before creating an anonymous delegate / function.

0
source share

There are already a few answers here explaining how you can avoid the lambda of "raising" your variable. Unfortunately, this does not solve your main problem. The inability to serialize a lambda has nothing to do with the lambda that "raised" your variable. If a lambda expression needs an instance of a non-serialized class, then it makes sense that it cannot be serialized.

Depending on what you are actually trying to do (I can't decide from your post), the solution would be to move the non-serializable part of the lambda outside.

For example, instead of:

 NonSerializable nonSerializable = new NonSerializable(); Func<string, string> prependAction = (x => nonSerializable.ToString() + x); 

using:

 NonSerializable nonSerializable = new NonSerializable(); string prefix = nonSerializable.ToString(); Func<string, string> prependAction = (x => prefix + x); 
0
source share

Now I get the problem: lambda refers to an contained class that cannot be serializable. Then do something like this:

 public void static Func<string, string> MakePrependAction(String prefix){ return (x => prefix + x); } 

(Note the static keyword.) Then the lambda should not refer to the containing class.

0
source share

How about this

 string prefix = "OLD:"; string _prefix=prefix; Func<string, string> prependAction = (x => _prefix + x); prefix = "NEW:"; Console.WriteLine(prependAction("brownie")); 
-one
source share

What about:

 string prefix = "OLD:"; string prefixCopy = prefix; Func<string, string> prependAction = (x => prefixCopy + x); prefix = "NEW:"; Console.WriteLine(prependAction("brownie")); 

?

-one
source share

Well, if we talk about the “problems” here, lambdas come from the world of functional programming, and in purely functional langauge programming there are no assignments, and therefore your problem will never arise because the prefix value will never change. I understand that C # considers It’s great to import ideas from functional programs (because FP is cool!), but it’s very difficult to do it beautifully, because C # will always and always be an imperative programming language.

-one
source share

All Articles