Using Closures to keep track of a variable: Good idea or dirty trick?
Asked Answered
A

3

2

Ok, i have a need to be able to keep track of value type objects which are properties on another object, which cannot be done without having those properties implement an IObservable interface or similar. Then i thought of closures and the famous example from Jon Skeet and how that prints out 9 (or 10) a bunch of times and not an ascending order of numbers. So i thought why not do this:

Class MyClass
{
    ...
    Func<MyValueType> variable;
    ...
    public void DoSomethingThatGetsCalledOften()
    {
        MyValueType blah = variable(); //error checking too not shown for brevity
        //use it
    }
    ...
}
... in some other consuming code ...
MyClass myClass = new MyClass();
myClass.variable = () => myOtherObject.MyOtherProperty;
//then myClass will get the current value of the variable whenever it needs it

Obviously this would require some understanding of how closures work, but my question is this: is this a good idea or a dirty hack and a misuse of the closure system?

Edit: Since some people seem to be misunderstanding what i'm trying to say, here's a console program which demonstrates it:

using System;
using System.Linq;

namespace Test
{
    class Program
    {
        public static void Main()
        {
            float myFloat = 5;
            Func<float> test = () => myFloat;
            Console.WriteLine(test());
            myFloat = 10;
            Console.WriteLine(test());
            Console.Read();
        }

    }
}

That will print out 5 then 10.

Albescent answered 26/12, 2009 at 3:25 Comment(3)
I've edited my answer; hopefully its applicability (or lack thereof!) to your code is a little clearer.Ito
Personally I think you should remove the first example from the question; it doesn't actually relate to your question.Deason
I suppose that's a good point.Albescent
G
8

You have stumbled upon the famous koan: Closures are a poor man's object. You are using Action<T> to substitute for a property getter of type T. Such a thing would be (slightly) less of a dirty trick in a more dynamic language, since it could be implemented by injecting a getter that’s decorated with your logging function, but in C# there isn’t an elegant way to monkeypatch someone’s property when they’re not expecting it.

Gavrilla answered 26/12, 2009 at 4:7 Comment(1)
I'm trying to roll a library and make it easy for users to have a class "focus" on an object, all the monkeypatching is done by the user :)Albescent
D
2

As a mechanism for obtaining the value fro a property, it'll work (but it won't provide any mechanism for noticing updates promptly). However, it depends on how you intend to use it. To do this conveniently you'll need to use a pile of lambdas in the code, or have some DynamicMethod / Expression code do it at runtime. In most cases, something more similar to reflection would be more convenient.

I wouldn't necessarily worry about the "value type" aspect; in most cases this isn't a bottleneck, despite the FUD - and it is generally a lot easier to handle such code with object than it is via generics or similar.

I have some code in my IDE that demonstrates DynamicMethod vs raw reflection (that I intend to blog some day soon), showing how reflection-based code doesn't have to be slow (or just use HyperDescriptor).

The other option is to implement the correct interfaces / add the correct events. Perhaps via PostSharp, perhaps via dynamic types (inheriting and overriding at runtime), perhaps with regular code.

Deason answered 26/12, 2009 at 7:51 Comment(0)
I
1

You would need to type your variable member as Func<MyValueType> (or another delegate that returns MyValueType), but you wouldn't be able to assign the value of blah in that manner. Just as with using the closure in the foreach loop above, it's only going to evaluate at a point in time. This isn't a way to keep your variable's value in sync with the other object. There is, in fact, no way to do that without either:

  • continuously monitoring the value of the other instance's property in some sort of loop, like a Timer
  • implementing a change notification event on the other instance's class

You would be able to implement a property like that (since a property is evaluated at every call), but then what's the sense in using a custom delegate, other than the fact that you don't have to know anything about the other instance.

Edit

I'll try to make this a little clearer. Using this code that you posted:

Class MyClass
{
    ...
    Action<MyValueType> variable;
    ...
    MyValueType blah = variable();
    //use it
    ...
}
...
MyClass myClass = new MyClass();
myClass.variable = () => myOtherObject.MyOtherProperty;

First, for this to be functional, variable should be Func<MyValueType>, not Action<MyValueType> (Func returns a value, Action does not; since you're trying to assign a value to a variable, you need the expression to return a value).

Second, the main issue with your approach is--assuming I'm reading your code correctly--you're attempting to assign the value of the instance variable blah to the evaluated value of variable() within the class declaration. This won't work for a couple of reasons:

  • assignments within class declarations cannot access instance members (which variable is)
  • the assignment of a variable within a class declaration just occurs upon construction of the object. Even if the first condition were present, you would simply get a NullReferenceException upon instantiating your object, since it would be trying to evaluate variable, which would be null at that time
  • even disregarding the first two, the value of blah would still only represent the evaluated value of variable() at whatever time it was evaluated. It would not "point to" that function and be automatically kept in sync, as it seems like you're trying to do.

If you aren't looking for some sort of automatic synchronization, then there's nothing stopping you from just keeping the Func<MyValueType> delegate around to evaluate; there's nothing particularly good or bad about that approach, and it isn't a closure unless the delegate (in your case a lambda expression) involves the use of a local variable.

Ito answered 26/12, 2009 at 3:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.