C # to generate anonymous delegates that use the same closure variable

I have a situation where I need to generate several similar anonymous delegates. Here is an example:

public void Foo(AnotherType theObj) { var shared = (SomeType)null; theObj.LoadThing += () => { if(shared == null) shared = LoadShared(); return shared.Thing; }; theObj.LoadOtherThing += () => { if(shared == null) shared = LoadShared(); return shared.OtherThing; }; // more event handlers here... } 

The problem I ran into is that my code is not very DRY. The contents of each of the EXTREMELY event handlers is similar and can be easily parameterized into the factory method. The only thing that prevents me from doing this is that each delegate needs to pass a link to the shared variable. I cannot pass shared to the factory method with the ref keyword, since you cannot create a closure around ref varaiable . Any ideas?

+6
source share
5 answers

There are no problems that cannot be solved by adding more abstraction. (*)

The pattern that you repeat over and over is a lazy loading pattern. This pattern is very capturing in the type, and in fact it was in version 4 of the structure. The documentation is here:

http://msdn.microsoft.com/en-us/library/dd642331.aspx

Then you can do something like:

 public void Foo(AnotherType theObj) { var shared = new Lazy<SomeType>(()=>LoadShared()); theObj.LoadThing += () => shared.Value.Thing; theObj.LoadOtherThing += () => shared.Value.OtherThing; // more event handlers here... } 

And you go there. At the first call to shared.Value value is loaded; Each subsequent period of time uses the cached value. Additional bonus: it is even thread safe if access to common values ​​will be available on several threads. (For details on what guarantees we provide regarding thread safety, see the documentation).


(*) Except, of course, for the problem: "I have too much abstraction."

+18
source

Add an extra layer of indirection. Create a class that is just a wrapper for the data you want to save:

 public class MyPointer<T> { public T Value{get;set;} } 

At the beginning of the method, enter a MyPointer<SomeType> and pass it to the factory. Now the MyPointer link MyPointer copied by value, so you cannot change the instance of MyPointer , but you can change the Value in each of the factory methods, and this is reflected elsewhere.

+1
source

What about:

 public void Foo(AnotherType theObj) { var shared = (SomeType)null; Action handler = () => { if(shared == null) shared = LoadShared(); return Shared.Thing; }; theObj.LoadThing += handler; theObj.LoadOtherThing += handler; // more event handlers here... } 

You can also put an action in a method and pass a parameter:

 public void Foo(AnotherType theObj) { var shared = (SomeType)null; theObj.LoadThing += () => Handle("LoadThing"); theObj.LoadOtherThing += () => Handle("LoadOtherThing"); // more event handlers here... } private T Handle<T>(T returnParameter) { if(shared == null) shared = LoadShared(); return returnParameter; } 
+1
source

I like Eric's method better, but here is another approach that shows up in some lisp fuel delirium, I think.

 var LoaderMaker = (Func<SomeType, int> thingGetter) => { return () => { if(shared == null) shared = LoadShared(); return thingGetter(shared); }; }; theObj.LoadThing = LoaderMaker(t => t.Thing); theObj.LoadOtherThing = LoaderMaker(t => t.OtherThing); 
+1
source

I suggest putting the repeating code in a separate function.

 public void Foo(AnotherType theObj) { var shared = (SomeType)null; Func<SomeType> getShared = () => { if(shared == null) shared = LoadShared(); return shared; }; // Or more compact Func<SomeType> getShared = () => shared ?? (shared = LoadShared()); theObj.LoadThing += () => getShared().Thing; theObj.LoadOtherThing += () => getShared().OtherThing; // more event handlers here... } 
+1
source

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


All Articles