2009-09-07 843 views
10

Tengo un código de auto-instanciación que me gustaría aplicar a unas 15 propiedades en una clase bastante grande. El código es similar a la siguiente, pero el tipo es diferente para cada instancia:C# Equivalente para Macros en C++ y usando Auto <> Propiedades

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

     return _propertyName; 
    } 
} 

Para repetir esto en C++ (ya que hay ~ 15 casos), habría utilizado un preprocesador macro pero note que C# no los admite.

Me pregunto si alguien tiene una recomendación sobre cómo hacer esto limpiamente en C#?

Respuesta

22

Esto podría hacer las cosas un poco más ordenado, se podría añadir este método para introducir un poco de reutilización:

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; 
} 
6

A pesar de que no resuelve directamente el problema, se puede echar un vistazo a la nueva clase Lazy<T> que se envía con .NET 4.0. Está diseñado específicamente para escenarios de inicialización perezosa.

16

Usted puede utilizar el operador ?? para simplificar el código en una línea:

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

Como nota al margen que probablemente evitar los campos protegidos. Si necesita establecer la propiedad de una clase derivada, preferiría crear un setter protegido.

+1

De acuerdo con mi experiencia en C++, generalmente he encontrado que configurar todo en 'protegido' por 'privado' es una mejor opción si quieres proteger tu aplicación en el futuro sin renunciar a la protección de miembros. Raramente uso privado. – Ryall

+2

@Kelix: pero en realidad está renunciando a la protección de miembros al proteger los campos. Ahora cualquier clase derivada tiene acceso completo a tus campos.Esto es algo similar a tener campos públicos, excepto que solo las clases derivadas y no todas las clases pueden acceder a los campos. –

+0

@Martin: Sí, este es el efecto deseado. A menos que explícitamente no quiera permitir el acceso en clases derivadas, lo marcaré como 'protegido'. En general, si va a hacer el esfuerzo de derivar una clase, de todos modos querrá agregar o alterar el comportamiento de alguna manera, ¿por qué no proporcionar acceso no restringido para hacerlo? Obviamente, si se tratara de una interfaz de acceso público (como una biblioteca de clases), entonces 'privado' sería mucho más adecuado. Simplemente no me gusta restringir el acceso donde no es necesario. – Ryall

11

se puede hacer una estructura genérica que se encarga de la creación perezosa:

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; 
    } 
} 
+1

Me gustaría utilizar una estructura genérica para evitar tener más objetos creados en el montón. –

+0

¿Qué sucede cuando T es una estructura? new() no lo descarta, y las estructuras AFAIK no pueden ser nulas. :) – Rytmis

+0

@Rytmis: Para manejar las estructuras tendría que tener una bandera separada que indicara si el valor fue creado o no, sin embargo, la iniciación floja para las estructuras parece ser una mala elección de diseño ... Agregaré un contraint para que que solo se puede usar con tipos de referencia. – Guffa

2

podría utilizar el muy pasado por alto T4 (plantillas de texto Transformation Toolkit) para generar el código. Se incluye con Visual Studio 2008.

Hubo un episodio Rocas 2009-06 .NET al respecto: "Peter Vogel uses Code Generation".

0

Intente utilizar Hashtable o Diccionario < cadena, ComplexType > para agrupar todas las propiedades. Algo como esto:

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]; 
    } 
} 
1

Se podría aplicar la inicialización perezosa de una manera similar a esto:

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(); 
     } 
    } 

que permitirá escribir código como este:

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

Los detalles de la inicialización es irrelevante, lo escribí así para mostrar que puede convertirlo en convertible transparente a la versión no perezosa y realizar la inicialización en la primera conversión. :)

Cuestiones relacionadas