views:

520

answers:

8

I have some auto-instantiation code which I would like to apply to about 15 properties in a fairly big class. The code is similar to the following but the type is different for each instance:

protected ComplexType _propertyName;
public ComplexType PropertyName
{
    get
    {
        if (_propertyName == null)
            _propertyName = new ComplexType();

        return _propertyName;
    }
}

To repeat this in C++ (as there are ~15 instances), I would have used a preprocessor macro but I notice C# doesn't support them.

I am wondering if anyone has a recommendation on how to do this cleanly in C#?

+19  A: 

This might make things a bit neater, you could add this method to introduce some reuse:

protected ComplexType _propertyName;
public ComplexType PropertyName
{
    get
    {
        return GetProperty(ref _propertyName);
    }
}
.
.
private T GetProperty<T>(ref T property) where T : new()
{
  if (property == null)
    property = new T();
  return property;
}
Charlie
+1  A: 

I have the same problem with INotifyProperties and I just make my own Code Snippets in Visual Studio.

vanja.
+6  A: 

Even though it doesn't directly solve your problem, you could have a look at the new Lazy<T> class that ships with .NET 4.0. It is specifically designed for lazy initialization scenarios.

Thomas Levesque
+15  A: 

You can use the ?? operator to simplify the code into one line:

protected ComplexType _propertyName;
public ComplexType PropertyName
{
  get
  {
    return _propertyName ?? (_propertyName = new ComplexType());
  }
}

As a side note I would probably avoid protected fields. If you need to set the property from a derived class I would rather create a protected setter.

Martin Liversage
From my experience in C++, I have generally found setting everything to 'protected' over 'private' to be a better move if you want to futureproof your app without giving up member protection. I rarely use private.
Nat Ryall
@Kelix: But you are actually giving up member protection by making fields protected. Now any derived class have full access to your fields. This is somewhat similar to having public fields except that only derived classes and not all classes can access the fields.
Martin Liversage
@Martin: Yes this is the desired effect. Unless I explicitly don't want to allow access in derived classes, I will mark it as 'protected'. Generally, if you are going to make the effort of deriving a class, you are going to want to add to or alter the behaviour in some way anyway and so why not provide unrestricted access to do so? Obviously if this were a publicly accessible interface (such as a class library), then 'private' would be much better suited. I just don't like restricting access where it's not necessary.
Nat Ryall
@Kelix - you have it backwards. You should be thinking "I don't like allowing access where it's not necessary".
Daniel Earwicker
+10  A: 

You can make a generic struct that handles the lazy creation:

public struct LazyCreate<T> where T : class, new() {
   private T _value;
   public T Value {
      get {
         if (_value == null) {
            _value = new T();
         }
         return _value;
      }
   }
}

protected LazyCreate<ComplexType> _propertyName;
public ComplexType PropertyName {
    get {
        return _propertyName.Value;
    }
}
Guffa
I would use at using a generic struct to advoid having more objects created on the heap.
Ian Ringrose
What happens when T is a struct? new() doesn't rule that out, and AFAIK structs can't be null. :)
Rytmis
@Rytmis: To handle structs it would have to have a separate flag that indicated if the value was created or not, however lazy initalisation for structs seems generally to be a bad design choise... I'll add a contraint so that it can only be used with reference types.
Guffa
@Ian: Good point, I dug up a very similar generic struct in our library as reference, and it should not be any problem changing the class to a struct. I wrote it as a class because structs are generally more complicated to implement correctly.
Guffa
+2  A: 

You could use the much overlooked T4 (Text Template Transformation Toolkit) to generate the code. It is included with Visual Studio 2008.

There was a recent .NET Rocks episode about it: "Peter Vogel uses Code Generation".

Peter Mortensen
A: 

Try to use Hashtable or Dictionary <string, ComplexType> to group all properties. Something like this:

protected Dictionaty<string, ComplexType> _properties = new Dictionaty<string, ComplexType>();
public ComplexType Property(string name)
{
    get
    {
        if (!properties.ContainsKey(name))
            _properties[name] = new ComplexType();

        return __properties[name];
    }
}
Veton
+1  A: 

You could implement lazy initialization in a manner similar to this:

  public class Lazy<T> where T : new()
   {
       private T _value;
       private bool _isInitialized;

       private T GetValue()
       {
           if (!_isInitialized)
           {
               _value = new T();
               _isInitialized = true;
           }

           return _value;
       }

       public static implicit operator T (Lazy<T> t)
       {
           return t.GetValue();
       }
   }

which would allow you to write code like this:

      private Lazy<ComplexType> _lazyCt = new Lazy<ComplexType>();
      public ComplexType LazyCt
      {
          get { return _lazyCt; }
      }

The specifics of the initialization are irrelevant, I wrote it like this to show that you can make it transparently convertible to the non-lazy version, and perform the initialization on the first conversion. :)

Rytmis