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