Automatic properties are great but the annoyance of not being able to set them automatically deters a lot of people from using them.
What we (read - I) want to be able to do is something like this:
double MyPie { get; set; } = "3.141";
string MyLove { get; set; } = "Pizza";
The default value attribute in System.ComponentModel
is of no help there, though a lot of us (read - me) tried that in the beginning:
[DefaultValue(0.3141)]
double MyPie { get; set; }
[DefaultValue("Pizza")]
string MyLove { get; set; }
Initializing the properties one by one in the constructor is at least as annoying as simply doing this:
double _myPie = 0.3141;
double MyPie { get { return _myPie; } set { _myPie = value; } }
double _myLove = "Pizza";
double MyLove { get { return _myLove; } set { _myLove = value; } }
Luckily, the nice little feature of extension methods allows us something not exactly optimal or fully elegant - but functional and a good exercise in C# clockwork.
So with just a bit of hammering, we can enable automatic reset properties to their declared System.ComponentModel.DefaultValueAttribute
if any was specified.
Here is some test code:
using System;
using System.ComponentModel;
using System.Text;
using System.Windows.Forms;
using CSharpUtils;
namespace MyAppNamespace
{
public class TestClass
{
[DefaultValue(0.3141)]
public double MyPie { get; set; }
[DefaultValue("Pizza")]
public string MyLove { get; set; }
public TestClass()
{
MessageBox.Show(string.Format("MyPie = {0}, MyLove = {1}",
MyPie, MyLove == null ? "null" : MyLove));
int ret = (this as Object).ResetPropsUsingDefaultAttributes(false);
MessageBox.Show(string.Format(
"MyPie = {0}, MyLove = {1}. {2} properties we set",
MyPie, MyLove == null ? "null" : MyLove, ret));
MyLove = "My dear wife";
MessageBox.Show(string.Format("MyPie = {0}, MyLove = {1}",
MyPie, MyLove == null ? "null" : MyLove));
ret = (this as Object).ResetPropsUsingDefaultAttributes(false);
MessageBox.Show(string.Format(
"MyPie = {0}, MyLove = {1}. {2} properties we set",
MyPie, MyLove == null ? "null" : MyLove, ret));
}
}
}
This (as you may have already guessed) is done using an extension method to the System.Object
class.
You will need a public static
class to define the extension method in. Here's the basic implementation source:
using System;
using System.Text;
using System.Reflection;
using System.ComponentModel;
namespace CSharpUtils
{
static class CSharpUtilsExtensionMethods
{
public static int ResetPropsUsingDefaultAttributes(
this Object oThis, bool initInheritedProperties)
{
int count = 0;
Type oType = oThis.GetType();
PropertyInfo[] infos = oType.GetProperties(BindingFlags.NonPublic |
BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo inf in infos)
{
if (initInheritedProperties || inf.DeclaringType.Equals(oType))
{
object[] oDefAtts = inf.GetCustomAttributes(
typeof(DefaultValueAttribute), initInheritedProperties);
if (oDefAtts.Length > 0)
{
DefaultValueAttribute defAtt =
oDefAtts[oDefAtts.Length - 1] as DefaultValueAttribute;
if (defAtt != null && defAtt.Value != null &&
!defAtt.Value.Equals(inf.GetValue(oThis,
BindingFlags.GetProperty, null, null, null)))
{
inf.SetValue(oThis, defAtt.Value, BindingFlags.SetProperty,
null, null, null);
count++;
}
}
}
}
return count;
}
}
}
All that's left now is using our test class (here again - with no message box and other nonsense).
using System;
using System.ComponentModel;
using System.Text;
using System.Windows.Forms;
using CSharpUtils;
namespace MyAppNamespace
{
public class TestClass
{
[DefaultValue(0.3141)]
public double MyPie { get; set; }
[DefaultValue("Pizza")]
public string MyLove { get; set; }
public void ResetProps()
{
(this as Object).ResetPropsUsingDefaultAttributes(false);
}
public TestClass()
{
ResetProps();
}
}
}
Notes:
- To use the extension method, the .cs file making the call must have a '
using
' declaration of the namespace where the extension method resides in. - Passing
true
for the parameter initInheritedProperties
also initializes any parent class properties - at the expense of possibly resetting already initialized properties. You can always extend the extension method to try and account for these cases. - Resetting the properties in this manner also works outside the constructor - anywhere in the program with proper access.
- There's still the annoyance of having to add a line in the constructor - but it is just one line and will not require additional modifications if properties are added, removed, or their type/default value is changed.
There you go, have fun.