September 20, 2011

Delegating Events & Event Serialisation Problems

Delegating an event to an underlying class:
...
private Helper helper;

// Normally implementation of add/remove are hidden but it is still possible to define them explicitly
public event EventHandler SomethingChanged
{
 add
 {
  this.helper.SomethingChanged += value;
 }
 remove
 {
  this.helper.SomethingChanged -= value;
 }
}
From the Event Accessors article by Stephen Toub, we get the following advice:

The trouble with events when it comes to .NET serialization is that they're backed by a private delegate field, and delegates are serializable. When you create a delegate for an instance method, the delegate maintains a reference to the instance on which to call that method, so when you serialize an object that contains an event, the formatter, while walking the object graph, will continue on down through the delegate attempting to serialize any instances registered with that delegate. A solution is to mark the backing store for the event with [NonSerializable]

Here is an example of the suggested solution:
[NonSerializable]
private EventHandler _myEvent;

September 14, 2011

Using Attributes for Property Meta Data

Another simple example of using Attributes and reflection. This time to place meta data on a property
[AttributeUsage(AttributeTargets.Property)]
[DebuggerDisplay("Name={Name}, Descr={Description}, Min={Min}, Max={Max}")]
public class DoublePropertyMetaData
    : System.Attribute
{
    public string Name { get; set; }
    public string Description { get; set; }
    public double Min { get; set; }
    public double Max { get; set; }
    public override string ToString()
    {
        string str = string.Format("Name={0}, Descr={1}, Min={2}, Max={3}", 
            Name, Description, Min.ToString(), Max.ToString());
        return str;
    }
}

public class PropertyTest
{
    [DoublePropertyMetaData(Name = "Valve Flow Rate", 
        Description = "Rate of flow through inlet valve", 
        Max = 12345.1d, Min = 0.0d)]
    public double ValveFlowRate { get; set; }
}

public class AttributesAsMetaDataTester
{
    public void Test1()
    {
        DoublePropertyMetaData propMetaData = GetDoubleMetaData(typeof(PropertyTest));
        Console.WriteLine("Result: " + propMetaData.ToString());
    }

    private static DoublePropertyMetaData GetDoubleMetaData(Type type)
    {
        PropertyInfo[] props = type.GetProperties();
        DoublePropertyMetaData[] attributes = new DoublePropertyMetaData[0];
        foreach (PropertyInfo prop in props)
        {
            attributes = (DoublePropertyMetaData[])prop.GetCustomAttributes(
                typeof(DoublePropertyMetaData), false);
        }
        return attributes[0];
    }
}