2012-08-23 12 views
18

¿Es posible algo como esto? No estoy asumiendo, pero se ve bien a mí:Alcance del valor de respaldo de la propiedad

class MyClass { 
    public int Foo { 
     get { return m_foo; } 
     set { 
      // Bounds checking, or other things that prevent the use 
      // of an auto-implemented property 
      m_foo = value; 
     } 

     // Put the backing field actually *in* the scope of the property 
     // so that the rest of the class cannot access it. 
     private int m_foo; 
    } 

    void Method() { 
     m_foo = 42; // Can't touch this! 
    } 
} 

Por supuesto que sé esta sintaxis es incorrecta, y esto no se compilará. Era hipotético-futuro-C# por el bien de retratar claramente mi idea. Me disculpo por la pregunta algo hipotética, pero es demasiado específica para Programmers.SE.

Algo como esto podría implementarse en el compilador que serviría para un propósito: permitir únicamente los accesores get y set de la propiedad para ver el campo, permitiendo esencialmente que la propiedad sea independiente (como propiedades auto implementadas) mientras permitiendo una lógica get/set adicional.

+2

¿Es esta una pregunta simple sí/no (porque entonces es ** no **), o quieres soluciones alternativas a este "problema"? –

+0

Tengo curiosidad por lo que estás tratando de lograr al hacer esto? –

+0

Sería muy interesante con soluciones alternativas. Podría ser una buena característica forzar el uso de la propiedad en lugar de la variable dentro de la clase misma. Porque esa debe ser la meta con esto, ¿verdad? –

Respuesta

31

La respuesta corta es no, eso no es posible hoy en C#.

Recibimos una solicitud de funciones como esta con bastante frecuencia; es una característica agradable en su forma más general. La forma más general es hacer más claro de por vida de local variable ortogonal a su alcance .

Solo para asegurarse de que esos términos sean claros: una variable es una ubicación de almacenamiento, posiblemente llamada.Cada variable tiene un de por vida: la cantidad de tiempo en el tiempo de ejecución en el que se garantiza que la variable se refiere al almacenamiento válido. El alcance de un nombre es la región de texto en la que se puede usar ese nombre; es un concepto de tiempo de compilación, no un concepto de tiempo de ejecución. Una variable local es una variable cuyo ámbito es un bloque de instrucción.

En muchos idiomas, la duración de una variable local está estrechamente vinculada a su alcance: cuando el control entra lógicamente en el alcance en tiempo de ejecución, comienza la vida útil y cuando sale del ámbito, finaliza la vida útil. Esto es cierto en C# con algunas salvedades notables:

  • La vida útil de un local, se podrán ampliar o truncada si el tiempo de ejecución puede determinar que al hacerlo no tiene consecuencias para la acción de código administrado en el hilo actual . Las acciones de otros hilos (como el hilo del finalizador) y el código no administrado en el hilo actual están definidos por la implementación.

  • La vida útil de un local que se encuentra en un bloque iterador, un método asíncrono o una variable externa cerrada de una función anónima puede ampliarse para que coincida o exceda la duración del iterador, tarea, delegado o árbol de expresión que lo usa.

Está claro que no es un requisito que la vida útil y el alcance de un local de ser atadas juntas en cualquier manera. Sería bueno si pudiéramos tener explícitamente locales que tengan la vida útil de una instancia o campo estático, pero el alcance de un local. C tiene esta característica; puedes hacer una variable local "estática". C# no. Su propuesta es esencialmente permitir una variable local dentro del bloque de la propiedad que tiene la duración de la instancia pero cuyo alcance está restringido al bloque.

Yo clasificaría esta función como "buena". Tenemos una lista de posibles características "buenas", literalmente, siempre y cuando no tengamos tiempo para implementar su brazo, por lo que no esperaría que esta llegue a la cima de la lista en el corto plazo. Gracias por los comentarios; nos ayuda a priorizar esa lista de alguna manera.

+1

Pregunta: ¿hay un cambio general simple en el diseño del lenguaje C# que desacoplaría variables? vida y alcance? Usted señala que las variables con ámbito de propiedad (pregunta de OP) y locales estáticos en C (su ejemplo) son casos específicos de desacoplamiento del alcance de vida, pero no puedo pensar cuál sería el caso general. – phoog

1

No, lo único que puede estar dentro del cuerpo de la propiedad es get y set.

2

De acuerdo con las especificaciones del lenguaje C# 4.0.

Sin embargo, a diferencia de los campos, las propiedades no indican las ubicaciones de almacenamiento. En su lugar, las propiedades tienen accesodores que especifican las sentencias que se ejecutarán cuando se lean o escriban los valores .

La adición de un campo requerirá una ubicación de memoria. Entonces no, esto no es posible.

+1

Entiendo lo que dices, pero eso realmente no se aplica aquí.Solo me preocupa el alcance léxico del campo, no dónde está almacenado en la memoria. Por supuesto, las propiedades solo se reducen a los métodos '_get()' y '_set()', por lo que el campo aún estaría contenido en el contexto de la clase. –

+0

@JonathonReinhart: si el tipo que lo contiene no es la propiedad, ¿qué utilidad proporciona el concepto? El tipo que lo contiene, p. la clase todavía puede acceder al campo privado. Esto también plantea la pregunta de si una propiedad puede incluso considerarse un tipo contenedor. –

+2

Solo digo que algo como esto podría implementarse en el compilador que serviría para un propósito: permitir solo que los accesores 'get' y' set' de la propiedad vean el campo, permitiendo esencialmente que la propiedad sea autónoma (como las propiedades implementadas automáticamente son) al tiempo que permiten una lógica adicional. –

1

Bueno, es bastante difícil de manejar, probablemente no muy eficiente, y no es algo que realmente usaría, pero técnicamente es una forma de oscurecer el campo de respaldo del resto de la clase.

public class MySuperAwesomeProperty<T> 
{ 
    private T backingField; 
    private Func<T, T> getter; 
    private Func<T, T> setter; 
    public MySuperAwesomeProperty(Func<T, T> getter, Func<T, T> setter) 
    { 
     this.getter = getter; 
     this.setter = setter; 
    } 

    public T Value 
    { 
     get 
     { 
      return getter(backingField); 
     } 
     set 
     { 
      backingField = setter(value); 
     } 
    } 
} 

public class Foo 
{ 
    public MySuperAwesomeProperty<int> Bar { get; private set; } 


    public Foo() 
    { 
     Bar = new MySuperAwesomeProperty<int>(
      value => value, value => { doStuff(); return value; }); 

     Bar.Value = 5; 

     Console.WriteLine(Bar.Value); 
    } 

    private void doStuff() 
    { 
     throw new NotImplementedException(); 
    } 
} 
+0

No hay nada que te impida configurar el campo de respaldo dentro de la clase MySuperAwesomeProperty. Esto también pierde el sentido de la pregunta original OMI. – user981225

+0

@ user981225 El objetivo de la clase de propiedad es que estará sellado y contenido en algún lugar, no para ser modificado. Otras clases simplemente lo usan, en lugar de una propiedad del valor real, para permitir getters/setters personalizados sin acceso al campo de respaldo. No impedirá que se modifique debido a la reflexión, pero a ese nivel se puede acceder incluso al campo de respaldo de una propiedad regular. ¿Cómo no es este el objetivo de la pregunta original? – Servy

+0

Mis disculpas, estás en lo cierto. SI editas la publicación para poder votar, lo haré. – user981225

6

Esta es mi opinión sobre ese:

public class WrappedField<T> 
{ 
    public class Internals 
    { 
     public T Value; 
    } 

    private readonly Internals _internals = new Internals(); 
    private readonly Func<Internals, T> _get; 
    private readonly Action<Internals, T> _set; 

    public T Value 
    { 
     get { return _get(_internals); } 
     set { _set(_internals, value); } 
    } 

    public WrappedField(Func<Internals, T> get, Action<Internals, T> set) 
    { 
     _get = get; 
     _set = set;    
    } 

    public WrappedField(Func<Internals, T> get, Action<Internals, T> set, T initialValue) 
     : this(get, set) 
    { 
     _set(_internals, initialValue); 
    } 
} 

Uso:

class Program 
{ 
    readonly WrappedField<int> _weight = new WrappedField<int>(
     i => i.Value,   // get 
     (i, v) => i.Value = v, // set 
     11);     // initialValue 

    static void Main(string[] args) 
    { 
     Program p = new Program(); 
     p._weight.Value = 10; 

     Console.WriteLine(p._weight.Value); 
    } 
} 
+0

también puede implementar una conversión implícita personalizada para que el código de usuario no se vea afectado y no tenga que usar varname.Value para establecer y obtener el valor de la propiedad – Sonia

2

Si desea evitar los genéricos, que podría siempre ocultar la _backingField y las comprobación de límites en una privada clase interna Incluso podría ocultarlo más haciendo parcial la clase externa. Por supuesto, debería haber algún delegado entre la clase externa y la interna, lo cual es un fastidio. Código para explicar mis pensamientos:

public partial class MyClass 
{ 
    public int Property 
    { 
     get { return _properties.Property; } 
     set { _properties.Property = value; } 
    } 

    public void Stuff() 
    { 
     // Can't get to _backingField... 
    } 
} 

public partial class MyClass 
{ 
    private readonly Properties _properties = new Properties(); 

    private class Properties 
    { 
     private int _backingField; 

     public int Property 
     { 
      get { return _backingField; } 
      set 
      { 
       // perform checks 
       _backingField = value; 
      } 
     } 
    } 
} 

Pero esto es un montón de código. Para justificar toda esa placa de la caldera, el problema original tiene que ser bastante grave ...

+0

¿Por qué alguien querría evitar los genéricos? – Grozz

+0

@Grozz No lo sé. Yo seguro que no. De hecho, subí la respuesta de Servy. Pero, ya había dos respuestas que estaban basadas en genéricos, así que pensé "what the h ** l" ... :-) –

Cuestiones relacionadas