¿Por qué es imposible anular una propiedad getter-only y añadir un setter? [cerrado]


¿Por qué crees (o, por qué es bueno que) Microsoft eligió no permitir:

public abstract class BaseClass
{
    public abstract int Bar { get;}
}

public class ConcreteClass : BaseClass
{
    public override int Bar
    {
        get { return 0; }
        set {}
    }
}

CS0546 ' ConcreteClass.Bar.set': no se puede anular porque ' BaseClass.Bar ' no tiene un accessor de conjunto overridable

Author: ripper234, 2008-09-17

16 answers

Porque el escritor de Baseclass ha declarado explícitamente que Bar tiene que ser una propiedad de solo lectura. No tiene sentido que las derivaciones rompan este contrato y lo hagan de lectura y escritura.

Estoy con Microsoft en este caso.
Digamos que soy un programador nuevo al que se le ha dicho que codifique contra la derivación de la clase Base. escribo algo que asume que Bar no se puede escribir (ya que la Baseclass declara explícitamente que es una propiedad get only). Ahora con su derivación, mi código puede romper. por ejemplo,

public class BarProvider
{ BaseClass _source;
  Bar _currentBar;

  public void setSource(BaseClass b)
  {
    _source = b;
    _currentBar = b.Bar;
  }

  public Bar getBar()
  { return _currentBar;  }
}

Dado que Bar no se puede establecer según la interfaz de la clase base, BarProvider asume que el almacenamiento en caché es algo seguro, ya que Bar no se puede modificar. Pero si set era posible en una derivación, esta clase podría servir valores obsoletos si alguien modificara externamente la propiedad Bar del objeto _source. El punto es 'Sé abierto, evita hacer cosas furtivas y sorprender a la gente '

Actualizar: Ilya Ryzhenkov pregunta '¿Por qué las interfaces no juegan por el mismas reglas entonces?' Hmm.. esto se pone más fangoso cuando lo pienso.
Una interfaz es un contrato que dice ' esperar que una implementación tenga una propiedad de lectura llamada Bar.' Personalmente Es mucho menos probable que haga esa suposición de solo lectura si veo una Interfaz. Cuando veo una propiedad get-only en una interfaz, la leo como 'Cualquier implementación expondría esta barra de atributos'... en una clase base, hace clic como 'Bar es una propiedad de solo lectura'. Por supuesto que técnicamente no estás rompiendo el contrato.. estás haciendo más. Así que tienes razón en cierto sentido.. Cerraría diciendo "haz lo más difícil posible para que surjan malentendidos".

 3
Author: Gishu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-20 06:26:09

Creo que la razón principal es simplemente que la sintaxis es demasiado explícita para que esto funcione de otra manera. Este código:

public override int MyProperty { get { ... } set { ... } }

Es bastante explícito que tanto el get como el set son sobreescrituras. No hay set en la clase base, por lo que el compilador se queja. Al igual que no se puede sobreescribir un método que no está definido en la clase base, tampoco se puede sobreescribir un setter.

Se podría decir que el compilador debe adivinar su intención y solo aplicar el override al método que puede ser anulado (es decir, el getter en este caso), pero esto va en contra de uno de los principios de diseño de C# - que el compilador no debe adivinar sus intenciones, porque puede adivinar mal sin que usted lo sepa.

Creo que la siguiente sintaxis podría funcionar bien, pero como Eric Lippert sigue diciendo, implementar incluso una característica menor como esta sigue siendo una gran cantidad de esfuerzo...

public int MyProperty
{
    override get { ... }
    set { ... }
}

O, para propiedades autoimplementadas,

public int MyProperty { override get; set; }
 28
Author: Roman Starkov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-03 12:06:27

Hoy me topé con el mismo problema y creo que tengo una razón muy válida para querer esto.

Primero me gustaría argumentar que tener una propiedad get-only no necesariamente se traduce en solo lectura. Lo interpreto como" From this interface/abtract you can get this value", eso no significa que alguna implementación de esa clase de interfaz/abstract no necesite que el usuario/programa establezca este valor explícitamente. Las clases abstractas sirven al propósito de implementar parte de lo necesario funcionalidad. No veo absolutamente ninguna razón por la que una clase heredada no pueda agregar un setter sin violar ningún contrato.

El siguiente es un ejemplo simplificado de lo que necesitaba hoy. Terminé teniendo que agregar un setter en mi interfaz solo para evitar esto. La razón para agregar el setter y no agregar, digamos, un método SetProp es que una implementación particular de la interfaz usó DataContract / DataMember para serializar Prop, lo que se habría hecho innecesariamente complicado si tuviera que añadir otra propiedad solo con el propósito de serialización.

interface ITest
{
    // Other stuff
    string Prop { get; }
}

// Implements other stuff
abstract class ATest : ITest
{
    abstract public string Prop { get; }
}

// This implementation of ITest needs the user to set the value of Prop
class BTest : ATest
{
    string foo = "BTest";
    public override string Prop
    {
        get { return foo; }
        set { foo = value; } // Not allowed. 'BTest.Prop.set': cannot override because 'ATest.Prop' does not have an overridable set accessor
    }
}

// This implementation of ITest generates the value for Prop itself
class CTest : ATest
{
    string foo = "CTest";
    public override string Prop
    {
        get { return foo; }
        // set; // Not needed
    }
}

Sé que esto es solo una publicación de "mis 2 centavos", pero siento que con el póster original y tratar de racionalizar que esto es algo bueno me parece extraño, especialmente teniendo en cuenta que las mismas limitaciones no se aplican cuando se hereda directamente de una interfaz.

También la mención sobre el uso de nuevo en lugar de sobreescritura no se aplica aquí, simplemente no funciona e incluso si lo hiciera, no daría usted el resultado deseado, es decir, un getter virtual como se describe en la interfaz.

 19
Author: JJJ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-17 06:09:11

Es posible

tl; dr- Puede anular un método get-only con un setter si lo desea. Es básicamente solo:

  1. Cree una propiedad new que tenga un get y un set usando el mismo nombre.

  2. Si no haces nada más, entonces el antiguo método get seguirá siendo llamado cuando la clase derivada sea llamada a través de su tipo base. Para arreglar esto, agregue una capa intermedia abstract que use override en la antigua get método para forzarlo a devolver el resultado del nuevo método get.

Esto nos permite anular propiedades con get/set incluso si carecían de uno en su definición básica.

Como bonificación, también puede cambiar el tipo de devolución si lo desea.

  • Si la definición base era get-only, entonces puede usar un tipo de retorno más derivado.

  • Si la definición base era set-solamente, entonces usted nos puede un retorno menos derivado tipo.

  • Si la definición base ya estaba get/set, entonces:

    • Puede utilizar un tipo de retorno más derivadosi lo haces set-solo;

    • Puede utilizar un tipo de retorno menos derivadosi lo haces get-solo.

En todos los casos, puede mantener el mismo tipo de retorno si lo desea. Los siguientes ejemplos utilizan el mismo tipo de retorno para simplicidad.

Situación: Preexistente get - solo propiedad

Tiene alguna estructura de clases que no puede modificar. Tal vez es solo una clase, o es un árbol de herencia preexistente. En cualquier caso, desea agregar un método set a una propiedad, pero no puede.

public abstract class A                     // Pre-existing class; can't modify
{
    public abstract int X { get; }          // You want a setter, but can't add it.
}
public class B : A                          // Pre-existing class; can't modify
{
    public override int X { get { return 0; } }
}

Problema: No se puede override el get - solo con get / set

Quieres override con una get/set propiedad, pero no se compilará.

public class C : B
{
    private int _x;
    public override int X
    {
        get { return _x; }
        set { _x = value; }   //  Won't compile
    }
}

Solución: Utilizar un abstract capa intermedia

Mientras que usted no puede directamente override con un get/set propiedad, usted puede :

  1. Crear un new get/set propiedad con el mismo nombre.

  2. override el antiguo método get con un accessor al nuevo método get para garantizar la coherencia.

Entonces, primero escribes la capa intermedia abstract:

public abstract class C : B
{
    //  Seal off the old getter.  From now on, its only job
    //  is to alias the new getter in the base classes.
    public sealed override int X { get { return this.XGetter; }  }
    protected abstract int XGetter { get; }
}

Entonces, escribes la clase que no compilaría antes. Va a compilar esta vez porque en realidad no estás override ' ing la propiedad get-only; en su lugar, la estás reemplazando usando la palabra clave new.

public class D : C
{
    private int _x;
    public new virtual int X { get { return this._x; } set { this._x = value; } }

    //  Ensure base classes (A,B,C) use the new get method.
    protected sealed override int XGetter { get { return this.X; } }
}

Resultado: Todo funciona!

Obviamente, esto funciona según lo previsto para D.

var test = new D();
Print(test.X);      // Prints "0", the default value of an int.

test.X = 7;
Print(test.X);      // Prints "7", as intended.

Todo sigue funcionando como se pretende cuando se ve D como una de sus clases base, por ejemplo, A o B. Pero, la razón por la que funciona podría ser un poco menos obvia.

var test = new D() as B;
//test.X = 7;       // This won't compile, because test looks like a B,
                    // and B still doesn't provide a visible setter.

Sin embargo, la definición de clase base de get sigue siendo en última instancia anulado por la definición de la clase derivada de get, por lo que sigue siendo completamente consistente.

var test = new D();
Print(test.X);      // Prints "0", the default value of an int.

var baseTest = test as A;
Print(test.X);      // Prints "7", as intended.

Discusión

Este método le permite agregar set métodos a get-solo propiedades. También puedes usarlo para hacer cosas como:

  1. Cambie cualquier propiedad a una propiedad get-only, set-only, oget-and-set, independientemente de lo que fuera en una clase base.

  2. Cambiar el tipo de retorno de un método derivado clase.

Los principales inconvenientes son que hay más codificación que hacer y un extra abstract class en el árbol de herencia. Esto puede ser un poco molesto con constructores que toman parámetros porque estos tienen que ser copiados / pegados en la capa intermedia.

 10
Author: Nat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-29 06:02:42

Estoy de acuerdo en que no ser capaz de anular un getter en un tipo derivado es un anti-patrón. Solo lectura especifica la falta de implementación, no un contrato de un funcional puro (implícito por la respuesta del voto superior).

Sospecho que Microsoft tenía esta limitación, ya sea porque se promovió el mismo concepto erróneo, o tal vez debido a la simplificación de la gramática; sin embargo, ahora que el alcance se puede aplicar para obtener o establecer individualmente, tal vez podemos esperar override puede ser demasiado.

El concepto erróneo indicado por la respuesta del voto superior, que una propiedad de solo lectura debería de alguna manera ser más "pura" que una propiedad de lectura/escritura es ridículo. Simplemente mire muchas propiedades comunes de solo lectura en el marco; el valor no es una constante / puramente funcional; por ejemplo, DateTime.Ahora es de solo lectura, pero cualquier cosa menos un valor funcional puro. Un intento de' almacenar en caché ' un valor de una propiedad de solo lectura suponiendo que devolverá el mismo valor la próxima vez es arriesgado.

En cualquier caso, he utilizado uno de los siguientes estrategias para superar esta limitación; ambas son menos que perfectas, pero le permitirán cojear más allá de esta deficiencia del lenguaje:

   class BaseType
   {
      public virtual T LastRequest { get {...} }
   }

   class DerivedTypeStrategy1
   {
      /// get or set the value returned by the LastRequest property.
      public bool T LastRequestValue { get; set; }

      public override T LastRequest { get { return LastRequestValue; } }
   }

   class DerivedTypeStrategy2
   {
      /// set the value returned by the LastRequest property.
      public bool SetLastRequest( T value ) { this._x = value; }

      public override T LastRequest { get { return _x; } }

      private bool _x;
   }
 8
Author: T.Tobler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-13 20:50:42

Tal vez podría solucionar el problema creando una nueva propiedad:

public new int Bar 
{            
    get { return 0; }
    set {}        
}

int IBase.Bar { 
  get { return Bar; }
}
 1
Author: Hallgrim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-17 12:37:06

Puedo entender todos sus puntos, pero efectivamente, las propiedades automáticas de C# 3.0 se vuelven inútiles en ese caso.

No puedes hacer nada como eso:

public class ConcreteClass : BaseClass
{
    public override int Bar
    {
        get;
        private set;
    }
}

IMO, C# no debe restringir tales escenarios. Es responsabilidad del desarrollador usarlo en consecuencia.

 1
Author: Thomas Danecker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-20 11:03:32

El problema es que, por cualquier razón, Microsoft decidió que debería haber tres tipos distintos de propiedades: solo lectura, solo escritura y lectura-escritura, solo una de las cuales puede existir con una firma dada en un contexto dado; las propiedades solo pueden ser anuladas por propiedades idénticas. Para hacer lo que quieras sería necesario crear dos propiedades con el mismo nombre y firma one una de las cuales era de solo lectura, y una de las cuales era de lectura-escritura.

Personalmente, deseo que todo el concepto de "propiedades" podría ser abolido, excepto que la sintaxis property-ish podría usarse como azúcar sintáctica para llamar a los métodos "get" y "set". Esto no solo facilitaría la opción' add set', sino que también permitiría que' get 'devuelva un tipo diferente de 'set'. Mientras que tal habilidad no se usaría terriblemente a menudo, a veces podría ser útil hacer que un método' get 'devuelva un objeto wrapper mientras que el' set ' podría aceptar un wrapper o datos reales.

 1
Author: supercat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-19 02:49:49

Aquí hay una solución para lograr esto usando la Reflexión:

var UpdatedGiftItem = // object value to update;

foreach (var proInfo in UpdatedGiftItem.GetType().GetProperties())
{
    var updatedValue = proInfo.GetValue(UpdatedGiftItem, null);
    var targetpropInfo = this.GiftItem.GetType().GetProperty(proInfo.Name);
    targetpropInfo.SetValue(this.GiftItem, updatedValue,null);
}

De esta manera podemos establecer el valor del objeto en una propiedad que es readonly. ¡Podría no funcionar en todos los escenarios!

 0
Author: user2514880,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-24 04:03:49

Debe alterar el título de su pregunta para detallar que su pregunta es únicamente en lo que respecta a anular una propiedad abstracta, o que su pregunta es en lo que respecta a anular generalmente la propiedad get-only de una clase.


If the former (overriding an abstract property)

Ese código es inútil. Una clase base por sí sola no debería decirte que estás obligado a anular una propiedad Get-Only (Tal vez una Interfaz). Una clase base proporciona una funcionalidad común que puede requerir una entrada específica de una clase implementadora. Por lo tanto, la funcionalidad común puede hacer llamadas a propiedades o métodos abstractos. En el caso dado, los métodos de funcionalidad común deberían pedirle que anule un método abstracto como:

public int GetBar(){}

Pero si no tienes control sobre eso, y la funcionalidad de la clase base lee desde su propia propiedad pública (weird), entonces simplemente haz esto:

public abstract class BaseClass
{
    public abstract int Bar { get; }
}

public class ConcreteClass : BaseClass
{
    private int _bar;
    public override int Bar
    {
        get { return _bar; }
    }
    public void SetBar(int value)
    {
        _bar = value;
    }
}

Quiero señalar el comentario (extraño) : Yo diría un la mejor práctica es que una clase no use sus propias propiedades públicas, sino que use sus campos privados/protegidos cuando existan. Así que este es un mejor patrón:

public abstract class BaseClass {
    protected int _bar;
    public int Bar { get { return _bar; } }
    protected void DoBaseStuff()
    {
        SetBar();
        //Do something with _bar;
    }
    protected abstract void SetBar();
}

public class ConcreteClass : BaseClass {
    protected override void SetBar() { _bar = 5; }
}

If the latter (overriding a class's get-only property)

Cada propiedad no abstracta tiene un setter. De lo contrario es inútil y no debería importarte usarlo. Microsoft no tiene que permitirte hacer lo que quieras. La razón es: el creador existe de una forma u otra, y usted puede lograr lo que quieres Veerryy fácilmente.

La clase base, o cualquier clase donde se puede leer una propiedad con {get;}, tiene ALGÚN tipo de setter expuesto para esa propiedad. Los metadatos se verán así:

public abstract class BaseClass
{
    public int Bar { get; }
}

Pero la implementación tendrá dos extremos del espectro de complejidad:

Menos complejo:

public abstract class BaseClass
{
    private int _bar;
    public int Bar { 
        get{
            return _bar;
        }}
    public void SetBar(int value) { _bar = value; }
}

Más complejo:

public abstract class BaseClass
{
    private int _foo;
    private int _baz;
    private int _wtf;
    private int _kthx;
    private int _lawl;

    public int Bar
    {
        get { return _foo * _baz + _kthx; }
    }
    public bool TryDoSomethingBaz(MyEnum whatever, int input)
    {
        switch (whatever)
        {
            case MyEnum.lol:
                _baz = _lawl + input;
                return true;
            case MyEnum.wtf:
                _baz = _wtf * input;
                break;
        }
        return false;
    }
    public void TryBlowThingsUp(DateTime when)
    {
        //Some Crazy Madeup Code
        _kthx = DaysSinceEaster(when);
    }
    public int DaysSinceEaster(DateTime when)
    {
        return 2; //<-- calculations
    }
}
public enum MyEnum
{
    lol,
    wtf,
}

Mi punto es que, de cualquier manera, tienes al armador expuesto. En su caso, es posible que desee anular int Bar debido a que no quieres que la clase base lo maneje, no tienes acceso a revisar cómo lo está manejando, o se te encomendó que ejecutara algún código real quick'n'dirty contra tu voluntad.

Tanto en este Último como en el Anterior (Conclusión)

En resumen: No es necesario que Microsoft cambie nada. Puede elegir cómo se configura su clase de implementación y, sin el constructor, usar todas o ninguna de la clase base.

 0
Author: Suamere,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-22 20:20:22

Solución para solo un pequeño subconjunto de casos de uso, pero sin embargo: en C# 6.0 el setter "readonly" se agrega automáticamente para las propiedades de solo getter anuladas.

public abstract class BaseClass
{
    public abstract int Bar { get; }
}

public class ConcreteClass : BaseClass
{
    public override int Bar { get; }

    public ConcreteClass(int bar)
    {
        Bar = bar;
    }
}
 0
Author: lxa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-09 17:28:10

Porque en el nivel IL, una propiedad de lectura/escritura se traduce en dos métodos (getter y setter).

Al sobrescribir, debe seguir soportando la interfaz subyacente. Si pudiera agregar un setter, efectivamente estaría agregando un nuevo método, que permanecería invisible para el mundo exterior, en lo que respecta a la interfaz de sus clases.

Cierto, agregar un nuevo método no estaría rompiendo la compatibilidad per se, pero como permanecería oculto, la decisión de no permitir esto tiene mucho sentido.

 -1
Author: Ishmaeel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-17 12:17:11

Porque eso rompería el concepto de encapsulación y ocultación de implementación. Considere el caso cuando crea una clase, la envía, y luego el consumidor de su clase se hace capaz de establecer una propiedad para la que originalmente proporciona un getter solamente. Interrumpiría efectivamente cualquier invariante de su clase en la que pueda confiar en su implementación.

 -1
Author: petr k.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-17 12:17:39

Porque una clase que tiene una propiedad de solo lectura (sin setter) probablemente tiene una buena razón para ello. Por ejemplo, es posible que no haya ningún almacén de datos subyacente. Al permitirle crear un setter se rompe el contrato establecido por la clase. Es sólo mala OOP.

 -1
Author: Joel Coehoorn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-17 13:15:24

Una propiedad de solo lectura en la clase base indica que esta propiedad representa un valor que siempre se puede determinar desde dentro de la clase (por ejemplo, un valor de enumeración que coincida con el contexto (db -) de un objeto). Así que la responsabilidad de determinar el valor permanece dentro de la clase.

Agregar un setter causaría un problema incómodo aquí: Se debe producir un error de validación si establece el valor a cualquier otra cosa que no sea el único valor posible que ya tiene.

Las reglas a menudo tienen excepciones, sin embargo. Es muy posible que, por ejemplo, en una clase derivada el contexto reduzca los posibles valores de enumeración a 3 de 10, sin embargo, el usuario de este objeto aún necesita decidir cuál es correcto. La clase derivada necesita delegar la responsabilidad de determinar el valor al usuario de este objeto. Importante darse cuenta {[6] } es que el usuario de este objeto debe ser muy consciente de esta excepción y asumir la responsabilidad de establecer la correcta valor.

Mi solución en este tipo de situaciones sería dejar la propiedad de solo lectura y agregar una nueva propiedad de lectura-escritura a la clase derivada para soportar la excepción. La anulación de la propiedad original simplemente devolverá el valor de la nueva propiedad. La nueva propiedad puede tener un nombre propio indicando el contexto de esta excepción correctamente.

Esto también apoya la observación válida: "haz lo más difícil posible para que surjan malentendidos" de Gishu.

 -1
Author: Remco te Wierik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-13 12:42:15

Esto no es imposible. Simplemente tienes que usar la palabra clave " new " en tu propiedad. Por ejemplo,

namespace {
    public class Base {
        private int _baseProperty = 0;

        public virtual int BaseProperty {
            get {
                return _baseProperty;
            }
        }

    }

    public class Test : Base {
        private int _testBaseProperty = 5;

        public new int BaseProperty {
            get {
                return _testBaseProperty;
            }
            set {
                _testBaseProperty = value;
            }
        }
    }
}

Parece que este enfoque satisface ambas partes de esta discusión. El uso de "nuevo" rompe el contrato entre la implementación de la clase base y la implementación de la subclase. Esto es necesario cuando una Clase puede tener múltiples contratos (ya sea a través de la interfaz o la clase base).

Espero que esto ayude

 -2
Author: mbolt35,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-27 19:00:38