Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

How To Work Around Passing a Property by Reference

0.00/5 (No votes)
27 Nov 2008 1  
How to work around passing a property by reference using delegates and wrapper class.

Introduction

This article will explain how to work around passing a .NET property similar to passing by ref (or out) parameter. This will often be useful when you would like to alter the actual property pointer. This method is purely for the purpose of having this workaround and not for performance improvement. Since this method uses reflection, it will have some minor overhead.

I hope you will enjoy this article.

Background

When something is passed by ref, its pointer is passed so functions using it will have direct access to the pointer for alteration. The challenge is to pass a property by ref. However, properties are translated into two functions when compiled: the get function and set function. Unlike other functions, the framework does not expose these functions at design time which limits one from accessing their delegates. 

Reference

Thanks to Alois Kraus for the original idea:

Property Wrapper

In order to access the property by ref, one must attempt to access the get and set delegate. We can accomplish this by using reflection. The PropertyInfo class contains the GetValue, and SetValue functions which then can be wrapped in a function to simulate the get and set delegates. We can get this by calling the GetProperty function of a given type.

Consider the class:

/// <summary>
/// Used as a wrapper class for passing a property
/// </summary>
/// <typeparam name="T">The type of the property return value</typeparam>
public class PropertyInvoker<T>
{
    private PropertyInfo propInfo;
    private object obj;

    /// <summary>
    /// Construct an property Invoker
    /// </summary>
    /// <param name="PropertyName">the property of an object we want to access</param>
    /// <param name="o">the object</param>
    public PropertyInvoker(string PropertyName, object o)
    {
        // set the instance object
        this.obj = o;
        // get the property information and check weather the type matches T
        this.propInfo = o.GetType().GetProperty(PropertyName, typeof(T));  
    }

    /// <summary>
    /// Wrapping the get and set into a property
    /// </summary>
    public T Property
    {
        get
        {
            return (T) propInfo.GetValue(obj, null);
        }
        set
        {
            propInfo.SetValue(obj, value, null);
        }
    }

    /// <summary>
    /// Wrapping the get function
    /// </summary>
    /// <returns></returns>
    public T GetProperty()
    {
        return this.Property;
    }

    /// <summary>
    /// Wrapping the set function
    /// </summary>
    /// <param name="value"></param>
    public void SetProperty(T value)
    {
        this.Property = value;
    }
}

The PropertyInvoker<T> class allows you to wrap a given property of an object, then you can pass this class into another function similar to by ref since you will now gain direct access to the property itself.

Examples

Consider the below class Person which contains properties that are objects, arrays, and primitive types.

/// <summary>
/// describes a person
/// </summary>
public class Person
{
    // object
    protected String _fullName;
    protected Person _father;
    protected Person _mother;
    // primitive
    protected int _age;

    // arrays
    protected Person[] _children;

    public String FullName
    {
        get {
            return this._fullName;
        }
        set {
            this._fullName = value;
        }
    }

    public Person Father
    {
        get {
            return this._father;
        }
        set {
            this._father = value;
        }
    }

    public Person Mother
    {
        get {
            return this._mother;
        }
        set {
            this._mother = value;
        }
    }

    public int Age
    {
        get {
            return this._age;
        }
        set {
            this._age = value;
        }
    }

    public Person[] Children
    {
        get {
            return this._children;
        }
        set {
            this._children = value;
        }
    }

    /// <summary>
    /// Default constructor
    /// </summary>
    /// <param name="Father"></param>
    /// <param name="Mother"></param>
    /// <param name="FullName"></param>
    /// <param name="Age"></param>
    /// <param name="Children"></param>
    public Person(Person Father, Person Mother, 
		String FullName, int Age, params Person[] Children)
    {
        this._father = Father;
        this._mother = Mother;
        this._fullName = FullName;
        this._age = Age;
        this._children = Children;
    }

    /// <summary>
    /// Create a person
    /// </summary>
    /// <param name="FullName"></param>
    /// <param name="Age"></param>
    public Person(String FullName, int Age)
        :this(null, null, FullName, Age)
    {
    }
}

Normally, it is enough to pass the person object to perform functionalities on the person object. However, sometimes it is required to perform an action on a property rather than the object itself. This means that the function is performed on the Type of the property such as a String manipulation, or an Array manipulation.

Normal by ref requires the pointer of the array:

..
// by ref resize of an array
Person[] children = new Person[] {new Person("Yang Yu", 30)};
// resize this array
Array.Resize<T>(ref children,2);
children[1] = new Person("Kendra Harwood", 28);
.. 

The above code has taken a normal array and resized it with by ref function which really constructs a new array and reassigns the pointer of children to that bigger array.

But if we want to perform this on the Children property of a Person, we really cannot pass the property in since it is an function, not a variable. This is where PropertyWrapper comes in handy.

..

        public static void Resize<T>(PropertyInvoker<T[]> pArray, int newSize)
        {
            // pre:
            if (pArray == null) return;

            // get the property value
            T[] array = pArray.Property;
            // perform by ref functionalities
            Array.Resize<T>(ref array, newSize);
            // set the property
            pArray.Property = array;
        }
..
 Person me = new Person("Yang Yu", 30);
  me.Children = new Person[] {new Person("Raine Yu", 1)};
  // call the resize using a property wrapper on the children property
  Resize<Person>(new PropertyInvoker<Person[]>("Children", me), 2);
  // set resized array 
  me.Children[1] = new Person("Josh Yu", 2);  

get and set Delegate Wrapper

We can use the GetProperty and SetProperty function of PropertyInvoker as delegates:

        public delegate string GetDelegate();
        public delegate void SetDelegate(string value);

Consider the following overloaded functions:

..
        public static void ReverseString(PropertyInvoker<String> strProperty)
        {
            // overload with the get property delegate
            ReverseString(strProperty.GetProperty, strProperty.SetProperty);
        }

        public static void ReverseString(GetDelegate get, SetDelegate set)
        {
            // invoke the get property method
            String str = get.Invoke();
            // call actual str reverse function
            set.Invoke(ReverseString(str));
        }

        public static String ReverseString(string str)
        {
            if (str.Length < 2) return str;

            char start = str[0], end = str[str.Length-1];
            // reverse inner
            return string.Concat(end, ReverseString
			(str.Substring(1, str.Length - 2)), start);
        }
..

The overloaded ReverseString takes the delegates as a parameter, which we use the get property and set property of the PropertyInvoker object. For example:

ReverseString(new PropertyInvoker<string>("FullName", new Person("Yang Yu", 33)));

Points of Interest

I hope this article has helped in your understanding of some design patterns around delegates and property passing as a parameter.

History

  • 27th November, 2008 - Article posted

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here