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:
public class PropertyInvoker<T>
{
private PropertyInfo propInfo;
private object obj;
public PropertyInvoker(string PropertyName, object o)
{
this.obj = o;
this.propInfo = o.GetType().GetProperty(PropertyName, typeof(T));
}
public T Property
{
get
{
return (T) propInfo.GetValue(obj, null);
}
set
{
propInfo.SetValue(obj, value, null);
}
}
public T GetProperty()
{
return this.Property;
}
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.
public class Person
{
protected String _fullName;
protected Person _father;
protected Person _mother;
protected int _age;
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;
}
}
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;
}
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:
..
Person[] children = new Person[] {new Person("Yang Yu", 30)};
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)
{
if (pArray == null) return;
T[] array = pArray.Property;
Array.Resize<T>(ref array, newSize);
pArray.Property = array;
}
..
Person me = new Person("Yang Yu", 30);
me.Children = new Person[] {new Person("Raine Yu", 1)};
Resize<Person>(new PropertyInvoker<Person[]>("Children", me), 2);
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)
{
ReverseString(strProperty.GetProperty, strProperty.SetProperty);
}
public static void ReverseString(GetDelegate get, SetDelegate set)
{
String str = get.Invoke();
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];
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