Dual Personality Properties with Lambdas in .NET
Some time ago (!) I wrote about an investigation that I had carried out into how a literal value could be mapped to and stored as a lambda expression/delegate in C#. This was important for me at the time because I needed to find some way to have a ‘value’ and a ‘value rule’ appear the same to callers. I wanted to define a type whose value when queried could either come from the evaluation of a previously specified ‘rule’ or could simply be retrieved from a real, previously stored value – without the caller knowing the difference at all.
This type of concept is common in functional and logic programming languages – in these languages the separation between functionality (code) and data is very blurry, what is data one minute can become code the next, and visa verse. It is harder to achieve (at a high level) in procedural languages but the addition of lambda expressions in .NET 3.0 brings us a a lot closer (in .NET 3.0)!
The following might help to demonstrate how such a type might work, imagine a generic class called DualProp whose job is to yield a typed value when requested, this value could have been previously stored as a real value or could be obtaind by evaluating a previously specified lambda expression:
[code lang="csharp"] // Create an integer property var speed = new DualProp(); // set it to 3 speed = 3; // Create an bool property, whose value depends // on the speed's value. var visible = new DualProp(); // visible only true if speed > 5 visible.Lambda = () => speed > 5; // Check visible's value ( this results in the // stored delegated being evaluated) if (visible == true) Console.WriteLine("It is visible"); else Console.WriteLine("It is invisible"); // Set speed to 8, sould now be visible speed = 8; // Try again, it should be visible now if (visible == true) Console.WriteLine("It is visible"); else Console.WriteLine("It is invisible"); // now just set visible's value to false visible = false; // And check it's value again, should be invisible if (visible == true) Console.WriteLine("It is visible"); else Console.WriteLine("It is invisible"); [/code]
Two properties are defined, an integer property called ‘speed’ and a bool property called ‘visible’. Visible’s value depends on the evaluation of the delegate compiled from the lambda expression that was assigned to it:
[code lang="csharp"] visible.Lambda = () => speed > 5; [/code]
So when queried, visible’s value is ‘true’ if speed’s current value is greater than 5.
This scheme should allow us to build up whole sets of properties some of whose values are real and others whose are dynamically computed based on the values of others. The values on which the lambda expressions depend can themselves be real or computed.
This type of set-up could be very handy, especially when used as part of a data driven user interface. A data driven user interface is typically laid out and behaves in accordance with some specified meta-data, this meta-data is usually static (no meta-rules, just meta-data!). Employing a scheme such as this should allow us to specify meta-data which can be dynamic, in that some of the meta-data can dynamically change based on applying ‘rules’ to some of the real data.
Imagine a CAD/CAM system for a laser drilling machine, it will have various logical tools defined that represent the different laser cutting tools onm the laser cutting machine. The laser tool’s various parameters can be viewed and changed on the CAD/CAM interface via a data driven property-page type interface. As usual the meta-data for this interface will specify a list of parameters along with some other data like:
- Name – Attribute’s Name
- Type – Attribute’s Type (Number/Boolean/String etc.)
- Visibility – Is this Attribute visible for editing
- ReadOnly – Can the attribute’s value be changed?
- Min – Attribute’s Minimum allowed value
- Max – Attribute’s Maximum allowed value
- HelpString – Some help text for the attribute
Rule based properties should allow us to do things like set the min & max values based on another parameter’s value or force a parameter to be read-only based on the value of yet another parameter (by specifying a rule for the visibility attribute). Anyway, I hope to expand on this in a future post.
Meanwhile, Here is the code for the DualProp class:
[code lang="csharp"] using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Linq.Expressions; // (c) 2009 Kevin Godden, Ridge Solutions. public class DualProp { // The property's delegate, which will // be evaluated whenever the property's value // is queried. private Func _lambda = null; // Map the passed value (of type T) to a lambda and // then compile to a delegate. private Func ToLambda(T val) { var body = Expression.Constant(val); var exp = Expression.Lambda>(body, null); return (Func)exp.Compile(); } // Given a value of type T, convert it // to a corresponding lambda delegate // and store it. private void SetValue(T val) { _lambda = ToLambda(val); } // Set for _lambda, used when we want // to specify a property 'rule' rather // than a literal value. public Func Lambda { set { _lambda = value; } } // Constructors public DualProp() { } // Construct with value public DualProp(T val) { SetValue(val); } // Construct with a 'rule' (lambda/delegate) public DualProp(Func lambda) { _lambda = lambda; } // Allows us to assign a value directly to // a DualProp i.e. --> // DualProp dualProperty = new DualProp(); // dualProperty = 10; public static implicit operator DualProp(T val) { return new DualProp(val); } // Allows us to query a DualProp's value directly // i.e. --> // DualProp dualProperty = new DualProp(10); // int value = dualProp; public static implicit operator T(DualProp sp) { return sp._lambda(); } } [/code]