Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# technique for generating anonymous delegates that share same closure variable

I have a situation where I need to generate a few similar anonymous delegates. Here's 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 trouble I'm having is that my code isn't very DRY. The contents of each of the event handlers is EXTREMELY similar, and could be easily parameterized into a factory method. The only thing preventing me from doing that is that each delegate needs to share the reference to the shared variable. I can't pass shared to a factory method with the ref keyword, as you can't create a closure around a ref varaiable. Any ideas?

like image 894
FMM Avatar asked Mar 23 '12 15:03

FMM


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is C full form?

Originally Answered: What is the full form of C ? C - Compiler . C is a general-purpose, high-level language that was originally developed by Dennis M. Ritchie to develop the UNIX operating system at Bell Labs. C was originally first implemented on the DEC PDP-11 computer in 1972.

How old is the letter C?

The letter c was applied by French orthographists in the 12th century to represent the sound ts in English, and this sound developed into the simpler sibilant s.

What is C language basics?

What is C? C is a general-purpose programming language created by Dennis Ritchie at the Bell Laboratories in 1972. It is a very popular language, despite being old. C is strongly associated with UNIX, as it was developed to write the UNIX operating system.


3 Answers

There's no problem that can't be solved by adding more abstraction. (*)

The pattern you are repeating over and over again is the "lazy loading" pattern. That pattern is highly amenable to being captured in a type, and in fact, it has been, in version 4 of the framework. Documentation here:

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

You could then 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 there you go. The first time shared.Value is accessed the value gets loaded; every subsequent time the cached value is used. Extra bonus: this is even threadsafe should the shared value be accessed on multiple threads. (See the documentation for details about precisely what guarantees we make regarding thread safety.)


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

like image 62
Eric Lippert Avatar answered Oct 11 '22 22:10

Eric Lippert


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

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

New up a MyPointer<SomeType> at the start of the method and pass it into the factory. Now the MyPointer reference is copied by value, so you can't change the MyPointer instance, but you can change the Value in each of the factory methods and it is reflected elsewhere.

like image 40
Servy Avatar answered Oct 11 '22 22:10

Servy


How 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 the 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;
}
like image 1
Arcturus Avatar answered Oct 11 '22 21:10

Arcturus