8

I have an architecture design problem which I think is appropriate for this site.

Note that I have made an EDIT to this post below, reflecting my latest potential solution to this problem.

General problem description:

My primary goal is to design a software library/program (C#) to automate a very particular type of microscopy experiments. Our experimental setups consists of many distinct hardware devices that generally fall into a limited number of categories:

  • Point detector
  • XY sample stage
  • Autofocus device
  • ...

The natural choice is to design an interface IDevice with some basic properties shared by all devices such as e.g. IDevice.Initialize(), IDevice.Name, ...

We can even design an abstract class, inheriting IDevice, e.g. DeviceBase implementing some functionalty common to all devices.

Likewise, we can also design interfaces for specific devices, each time implementing the common stuff (IXYSampleStage might hold e.g. IXYSampleStage.Move(posX, posY) or IXYSampleStage.AxisXStepSize).

Ultimately, I can implement all these interfaces in the final classes representing specific devices. I think I know what to do there.

However, not all devices of a class are exactly the same. Some manufacturers offer rich functionality on top of the standard stuff. An XY Stage might have e.g. any number of optional/non-standard parameters to set such as e.g. channel delays, PID control values or whatever.

To tackle this,

I need a mechanism to add additional properties (not methods, properties will suffice) to specific device classes. Higher level code, interacting with the devices should understand these added properties, be able to get/set them, however many there might be.

So much for the basic design problem.

What exists already

There is an OS software program, Micro Manager, which has such a system implemented, be it in C++ (full source here and no, I cannot use this SW directly for my purposes):

class PropertyBase
{
public:
   virtual ~PropertyBase() {}

   // property type
   virtual PropertyType GetType() = 0;

   // setting and getting values
   virtual bool Set(double dVal) = 0;
   virtual bool Set(long lVal) = 0;
   virtual bool Set(const char* Val) = 0;

   virtual bool Get(double& dVal) const = 0;
   virtual bool Get(long& lVal) const = 0;
   virtual bool Get(std::string& strVal) const = 0;

   // Limits
   virtual bool HasLimits() const = 0;
   virtual double GetLowerLimit() const = 0;
   virtual double GetUpperLimit() const = 0;
   virtual bool SetLimits(double lowerLimit, double upperLimit) = 0;

   // Some more stuff left out for brevity
   ...
};

Subsequently, the Property class implements some of the pure virtual functions related to the limits.

Next, StringProperty, IntegerProperty and FloatProperty implement the Get()/Set() methods. These kinds of properties are defined in an enum which allows only these three kinds of property.

Properties can next be added to a PropertyCollection which is basically a kind of dictionary that is part of a device class.

This all works nicely. We use MM a lot in the lab for other types of experiments but when trying to do similar things in my C# solution I stumbled about a few more fundamental questions. Most of these have to do with not being quite sure how to leverage specific features offered by C#/.NET (generic but definitely also dynamics, ...) to perhaps improve the existing C++ implementation.

The questions:

First question:

To limit the types of properties allowed, MM defines an enum:

    enum PropertyType {
      Undef,
      String,
      Float,
      Integer
    };

Whereas C# has reflection and a lot of built-in support for types/generics. I therefore considered this:

public interface IProperty<T>
{
   public T value { get; set; }

   ...
}

I could next do an abstract PropertyBase<T> and ultimately

IntegerProperty : PropertyBase<int>
StringProperty : PropertyBase<string>
FloatProperty : PropertyBase<double>

But this gets me in trouble when trying to put them into a collection because in .NET following is not possible:

PropertyCollection : Dictionary<string, IProperty<T>>

Unless, perhaps, I adopt this strategy, i.e. have a non generic base class that only returns a type and then yet more base classes to actually implement my property system. However, this seems convoluted to me. Is there a better way?

I think that utilising the generic language features might allow me to do away with needing all these Get()/Set() methods in the MM example above, as I would be able to know the backing type of my property value.

I realise this is a very broad question but, there is a big gap between understanding the basics of some language features and being able to fully make the correct design decisions from the outset.

Second question

I am contemplating making my final device classes all inherit from a kind of DynamicDictionary (e.g. similar to the one from here):

    public class DynamicDictionary : DynamicObject
    {
        internal readonly Dictionary<string, object> SourceItems;

        public DynamicDictionary(Dictionary<string, object> sourceItems)
        {
            SourceItems = sourceItems;
        }
        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            if (SourceItems.ContainsKey(binder.Name))
            {
                SourceItems[binder.Name.ToLower()] = value;
            }
            else
            {
                SourceItems.Add(binder.Name.ToLower(), value);
            }
            return true;
        }
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            if (SourceItems != null)
            {
                if (SourceItems.TryGetValue(binder.Name.ToLower(), out result))
                {
                    return true;
                }
            }
            result = null;
            return true;
        }
    }
}

but instead of <string, object> I would then like to use <string, MyPropertyBase> where the string would be the alias for my property (which would correspond to the design choice from Q1).

Would doing such a thing make sense from a design standpoint?

I would actually only need to add custom properties to devices at design time (i.e. when writing a custom device driver). At runtime, I would only need to inspect my custom properties or get/set their values.

the C++ MM solves this by using a bunch of CreateProperty("alias", Property) methods in the device constructor, which again, works so maybe using dynamics here is overkill. But again, I am wondering if somebody could provide insight in possible advantages but certainly also disadvantages of going down the dynamic route.

To summarize

Are there .NET features (4.5) which I could leverage to implement some of the MM concepts in a more elegant way with a focus on the above mentioned specific matters?

Again, I do realise this is a very broad question and perhaps some of the possible answers are more a matter of taste/style than anything els but if so (this making the question unsuited for this platform), please provide feedback/comments such that I can adjust accordingly.

EDIT

I have spent some more thinking about this and tried to solve it in the following way:

First, an enum specifying the allowed propertytypes:

public enum PropertyType
{
    Undefined,
    String,
    Float,
    Integer
}

Second, an interface IPropertyType that is responsible for checking values fed to a property of a specific type:

public interface IPropertyType
{   
    Type BackingType { get; }

    PropertyType TypeAlias { get; }

    bool IsValueTypeValid(object value);
}

Then, a base class implementing IPropertyType:

public bool IsValueTypeValid(object value)
    {
        if (value == null)
        {
            return true;
        }

        Type type = value.GetType();

        if (type == this.BackingType)
        {
            return true;
        }

        return false;
    }

...

public Type BackingType
    {
        get
        {
            switch (this.TypeAlias)
            {
                case PropertyType.Integer:
                    return typeof(long);
                case PropertyType.Float:
                    return typeof(double);
                case PropertyType.String:
                    return typeof(string);
                default:
                    return null;
            }
        }
    }

I then have three IPropertyType: StringPropertyType, IntegerPropertyType and FloatPropertyType where each time, the property PropertyTypeAlias is set to one of the enum values.

I can now add my IPropertyType to IProperty types:

public interface IProperty
{
    string Alias { get; }

    /// If we want to limit the property to discrete values.
    Dictionary<string, object> AllowedValues { get; }

    bool HasLimits { get; }

    bool HasValue { get; }

    bool IsReadOnly { get; }

    /// Callback for HW operations using the property value.
    Func<PropertyFuncType, bool> PropertyFunction { set; }

    PropertyType TypeAlias { get; }

    object Value { get; set; }

    void AddAllowedValue(string alias, object value);

    void ClearAllowedValues();

    // On numeric properties, it might be usefull to limit the range of values.
    void SetLimits(object lowerLimit, object upperLimit);

    // These will do stuff on the HW...
    bool TryApply();

    bool TryUpdate();
}

Here, the Value setter will run the validation on input value as defined by the IPropertyType.

IProperty is almost fully implemented in PropertyBase which also has a field of IPropertyType which gets set upon instance creation (and cannot be changed thereafter; a property cannot change type.

From the base class e.g.:

    public object Value
    {
        get
        {
            return this.storedvalue;
        }

        set
        {
            if (this.propertyType.IsValueTypeValid(value) && this.IsValueAllowed(value))
            {
                this.storedvalue = value;
            }
        }
    }

That only leaves specific IProperty classes: StringProperty, IntegerProperty and FloatProperty where there is only one override, SetLimits() because it relies on comparison of values. Everything else can be in the base class:

public override void SetLimits(object lowerlimit, object upperlimit)
    {
        // Is false by default
        this.HasLimits = false;

        // Makes no sense to impose limits on a property with discrete values.
        if (this.AllowedValues.Count != 0)
        {
            // If the passed objects are in fact doubles, we can proceed to check them.
            if (this.IsValueTypeValid(lowerlimit) && this.IsValueTypeValid(upperlimit))
            {
                // In order to allow comparison we need to cast objects to double.
                double lowerdouble = (double)lowerlimit;
                double upperdouble = (double)upperlimit;

                // Lower limit cannot be bigger than upper limit.
                if (lowerdouble < upperdouble)
                {
                    // Passed values are OK, so set them.
                    this.LowerLimit = lowerdouble;
                    this.UpperLimit = upperdouble;

                    this.HasLimits = true;
                }
            }
        }
    }

This way, I think I have a system that allows me to have a collection of IProperty that can actually hold data of different types (int, float or string) where for each type of IProperty I have the ability to implement value limits etc (i.e. stuff that is different, depending on the data type)...

However, I still have the impression that there are either better ways to do this sort of thing or that I am over engineering things.

Therefore, any feedback on this is still welcome.

EDIT 2

After more searching online I believe the last bits of code posted here in the previous edit are leaning towards the Adaptive Object Model "pattern" (if it really is that).

However, most resources on this topic seem to be quite old and I'm wondering if there are now better ways to achieve these types of things.

14
  • So, the point is to just get/set some device properties through GUI? Is there any "logic" that depends on specific device?
    – Euphoric
    Aug 29, 2014 at 8:32
  • Just one little comment - you don't have to do the ContainsKey check - simply setting a key will create a new entry if necessary.
    – Den
    Aug 29, 2014 at 8:33
  • 1
    The ExpandoObject might be worth looking at (no limits setting though): stackoverflow.com/questions/1653046/…
    – Den
    Aug 29, 2014 at 8:36
  • @Euphoric that would indeed be the case. Until now, we relied on hard-coding everything, requiring a lot of code re-writing for the UI, a major pain. The design here would be complemented with a UI for all device properties that is created dynamically so to speak. Until now the dynamic properties would only be used to set HW properties. Common operations/logic are more or less standard across devices (move, zero, calibrate, ...). The approach is necessary because different manufacturers do some things differently in HW with more/less parameters.
    – Kris
    Aug 29, 2014 at 8:36
  • 1
    @Kris "You can also ... add new elements by setting the value of a key that does not exist ..." msdn.microsoft.com/en-us/library/9tee9ht2(v=vs.110).aspx
    – Den
    Aug 29, 2014 at 12:40

5 Answers 5

14

You're making a type system on top of the existing type system. There is very little benefit to strongly-typed properties that cannot be referenced in compiled code. I have seen this approach used before and it ends up being a mess.

Just use a key-value store in a single property called ExtendedProperties or some such.

In other words, you are trying to avoid Dictionary<string, object>, but in that effort you are re-inventing the .Net type system.

Attach a property called ExtendedProperties of type Dictionary<string, object> and be done with it. The limitations of loosely-typed data are less costly than the road you're going down.

0
5

If properties will only ever be used in GUI and as read/set for HW, then there is no need for generics or reflection. Just use simple OOP classes. What you should focus is not how internals of the properties will be, but on how they will be used. How do you want to have those properties editable from GUI or how you want to save or retrieve them to the HW device?

public interface PropertyBase
{
    string Name {get;} // name of this property

    void SetToDevice(); // sets the value to device
    void ReadFromDevice(); // read from device

    GUIEditor GetEditor(); // returns editor for this property
}

public class PropertyContainer // device can implement or contain this
{
    public List<PropertyBase> Properties;
}

public interface IntProperty : PropertyBase
{
    public IDevice Device {get;set;}
    public string Name {get;set;} // name of this property

    public int Value {get;set;}

    void SetToDevice()
    {
        Device.SetInt(Name, Value); 
    }
    void ReadFromDevice()
    {
        Value = Device.ReadInt(Name);
    }

    GUIEditor GetEditor()
    {
        return new IntEditor(this); // IntEditor can get/set value itself
    }
}

The GUIEditor will be way for GUI to know how to edit the specific property. If you have dependency problems, you could use Visitor pattern to move this logic into View. The major advantage of this approach is that you have are open to add new device-specific properties without having to change existing code.

1
  • This is the kind of feedback I am looking for. It indeed never occurred to me to have an approach where I focus on the consumption of the property much rather than the contents. I'm still trying to assess the impact of this solution but thanks already for the nice insight.
    – Kris
    Aug 29, 2014 at 9:32
0

What you want pretty much sounds like WPF´s dependency properties and attached properties. You may want to look into the design of System.Windows.DependencyObject as a base class and System.Windows.DependencyProperty as the property class. It does involve a bit of overhead, but is really quite flexible.

3
  • After some more digging around the internet, I found that actually many Content Management Systems for websites such as e.g. Umbraco (www.umbraco.org) seem to use the approach I intended: Most CMS allow users to define custom document types (home page, text page, contact page, ...) with many common properties (URL, Id, guid, ...) but also custom properties (an intro text for home page, an address field for contact page, ...). Much of the stuff I edited into my Q at the end is based on what I learned from looking at such CMS systems, Umbraco in particular.
    – Kris
    Sep 17, 2014 at 14:14
  • I think the WPF dependency properties system is more elaborate still...
    – Kris
    Sep 17, 2014 at 14:15
  • This link is relevant btw (also amended to the question): joeyoder.com/Research/metadata/WICSA3/…
    – Kris
    Sep 17, 2014 at 14:23
0

I had the same kind of issue. I have a "simple" plugin system where I have a workspace where i can drag and drop widgets in the form of plugin. I wanted to give flexibility (still working on the full functionality) to the plugin creator to add a set of properties attached to the plugin. As i'm using an MVP approach I created an interface to access the property

public interface IPluginControlSettingsPropertyAccessors<T>
{
    bool Validator(T _Source);

    bool Setter(T _Source);

    bool Getter(out T _Result);
}

and for each property the plugin exposes several accessors of different types with the constraint to at least expose a

IPluginControlSettingsPropertyAccessors<string> 

for each property.

Then it depends on the View to actually implement a way to handle certain types of properties e.g. you can have:

if (l_Property is IPluginControlSettingsPropertyAccessors<Enum>)
{
     View.AddEnumHandler(l_Property);
}
else if (l_Property is IPluginControlSettingsPropertyAccessors<String>)
{
    View.AddStringHandler(l_Property);
}

As you can see this disconnects an object and its properties from how they are being shown on the GUI. With this approach a simple GUI could simply treat all properties in their string form for visualization purposes and defer string handling to the property setter and validator. If an object has a property which exposes

IPluginControlSettingsPropertyAccessors<String>
IPluginControlSettingsPropertyAccessors<Color>

and your GUI only support the string type property then it will have a textbox which (depending on the string validator) might accept only colors in the #FFFFFF format. A more complex GUI might instead support IPluginControlSettingsPropertyAccessors and provide a color pick.

I am quite new to C# too and I could not come up with a better solution than this regarding an MVP pattern but so far it has proven quite easy to maintain.

0

You could use an ExpandoObject to contain your properties. This has the benefit of allowing you to use any property name and type at compile-time, so you can add them and read them as you see fit, as long as you can prevent yourself from getting confused. The down side is that you do not get the same static type checking that you would if you defined interfaces for all of your properties.

Here is an example where Foo and Bar are the dynamic, device-specific properties of devices.

First, define an IDevice interface that has your base properties:

public interface IDevice
{
    string Name { get; }
}

And an interface that exposes a dynamic to contain the properties:

public interface IPropertyContainer 
{
    dynamic Properties { get; }
}

Now tie them together in your base class:

public class DeviceBase : IDevice, IPropertyContainer
{
    public string Name { get; set; }

    protected dynamic _properties = new ExpandoObject();

    public dynamic Properties
    {
        get
        {
            return _properties;
        }
    }
}

When you define your specific device classes, populate the ExpandoObject with your dynamic properties:

public class MyDeviceA : DeviceBase
{
    public MyDeviceA()
    {
        _properties.Foo = "Foo";
    }
}

public class MyDeviceB : DeviceBase
{
    public MyDeviceB()
    {
        _properties.Bar = "Bar";
    }
}

Notice that nowhere do we declare that Bar and Foo are valid properties. It's inferred from the usage... which is both convenient and a little dangerous.

A test program:

public class Program
{
    public static void Main()
    {
        DeviceBase deviceA = new MyDeviceA();
        Console.WriteLine(deviceA.Properties.Foo);

        DeviceBase deviceB = new MyDeviceB();
        Console.WriteLine(deviceB.Properties.Bar);
    }
}

Actual output:

Foo
Bar

Here is a link to a working example on DotNetFiddle

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.