Sunday, April 26, 2015

Coding: Synchronizing .NET Properties

I wrote a short class that can synchronize properties for any collection of .NET objects.  The type needs to implement INotifyPropertyChanged, but other than that there is no special requirements to allow a type to be synchronized with this mechanism.

The full class definition follows.  A usage example would be give, a class Car with a writeable property MaxSpeed, a synchronizer can be created like this:
    new PropertySynchronizer(car => car.MaxSpeed)
then a collection of objects of class Car can be added using AddRange; all of these will have there MaxSpeed synchronized to the first items MaxSpeed.  Any change to one of the them will change all of them.
//////   span="""T" the type of the object to synchronize
///   span="">"PropertyType" the type of the Property to be synchronized
public class PropertySynchronizer where T : INotifyPropertyChanged
{
Note that the constraint where T : INotifyPropertyChanged can be relaxed; if INotifyPropertyChanged is not implemented then there will not be updates for the class changing.
    /// constructs a Synchronizer for object type T and PropertyType.
    /// expression provided to identify the property    /// "forMemberLambda" expression of the form 'obj => obj.Property'     public PropertySynchronizer(Expression<Func> forMemberLambda)     {         // store the member expression and compiled function         _forMemberLambda = forMemberLambda;         _forMember = _forMemberLambda.Compile();         // test to get the member info         LambdaExpression lambdaExpression = (LambdaExpression)_forMemberLambda;         MemberExpression memberExpression = (MemberExpression)lambdaExpression.Body;         _memberInfo = memberExpression.Member;     }     Expression<Func> _forMemberLambda;     Func _forMember;     MemberInfo _memberInfo; 
The constructor takes an expression representing the member property, and uses it to extract the MemberInfo as well as to create a compiled function.  Both are used to access the property.   
    /// adds a collection of objects to be synchronized
    /// "newObject" the collection of objects     public void AddRange(IEnumerable<T> newObjects)     {         // for each of the objects in the collection...         foreach (T newObject in newObjects)         {             // if it is not the first object             if (_synchronizedObjects.Count() > 0)             {                 // then set the property value                 SetPropertyValue(newObject, _forMember(_synchronizedObjects.First()));             }             // set up the property change event             INotifyPropertyChanged notifyPropertyChanged = 
                   newObject as INotifyPropertyChanged;
            if (notifyPropertyChanged != null)
            {
                notifyPropertyChanged.PropertyChanged += 
                     (s, e) => OnSyncObjectPropertyChanged(s, e);
            }
 
            // add the object to the list
            _synchronizedObjects.Add(newObject);
        }
    }
    List _synchronizedObjects = new List();
AddRange adds a collection of type T objects, setting the property values and also adding the change event handlers
    /// removes all objects from synchronization    public void RemoveAll()
    {
        // iterate over objects 
        foreach (T existingObject in _synchronizedObjects)
        {
            // remove property change event handler
            INotifyPropertyChanged notifyPropertyChanged = 
                     existingObject as INotifyPropertyChanged;
            if (notifyPropertyChanged != null)
            {
                notifyPropertyChanged.PropertyChanged -= 
                    (s, e) => OnSyncObjectPropertyChanged(s, e);
            }
        }
 
        // clear the collection
        _synchronizedObjects.Clear();
    }
RemoveAll removes all synchronized objects from the collection
    /// sets the property value for an object    /// "forObject" object of type T whose property is to be set     /// "value" the value to set     public void SetPropertyValue(T forObject, PropertyType value)     {         // standard flags for property invoke         BindingFlags flags = BindingFlags.DeclaredOnly                                    | BindingFlags.Public                                    | BindingFlags.NonPublic                                    | BindingFlags.Instance                                    | BindingFlags.SetProperty;         // set the member value         _memberInfo.DeclaringType.InvokeMember(_memberInfo.Name, flags,             null, forObject, new object[] { value });     }
SetPropertyValue sets the property value for a given T object using InvokeMember
    /// respond to property change    /// "sender" the object originating the change     /// "args" change event args     public void OnSyncObjectPropertyChanged(object sender, PropertyChangedEventArgs args)     {         // check that the property of interest is the one being triggered         if (args.PropertyName.CompareTo(_memberInfo.Name) == 0)         {             // are we already updating this property?  then skip             if (_performingUpdate)                 return;             // set flag to prevent recursion             _performingUpdate = true;             // get the value to be set             PropertyType value = _forMember((T)sender);             foreach (T otherObject in _synchronizedObjects)             {                 if (!otherObject.Equals((T)sender))                 {                     SetPropertyValue(otherObject, value);                 }             }             // done with update             _performingUpdate = false;         }     }     // flag to ensure that we don't recurse     bool _performingUpdate = false; }
OnSyncObjectPropertyChanged responds to property changes by updating the other objects in the collection to have the same property value as the sender of the change event.

Monday, June 3, 2013

IHERO / DICOM Prediction Market

I've been researching prediction markets lately, and have been thinking of ways that a prediction market could be useful in the medical imaging domain (particularly the RT domain).  A valuable set of events to be able to predict would be the support by various vendors for various DICOM objects or IHERO profiles.  The events could be broken down to the level of specific transactions to be supported (for IHERO profiles) or even support for particular modules or tags within a given DICOM class.

The events could also include device and schedules, so for instance "Odds that SIEMENS will support SRO objects for patient positioning by end of 2012" which would be a contract that pays a nominal $1 if SIEMENS actually provides support for the object.  That contract could then be supported by subcontracts representing potential implementation strategies that the support may assume.