65.9K
CodeProject is changing. Read more.
Home

C# Automatic Property Default Value Extension Methods

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (6 votes)

Aug 21, 2011

CPOL

2 min read

viewsIcon

54203

This article shows how to automatically initialize/reset automatic properties' default values.

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;
      //BindingFlags.Static
      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.