c# - tipos - ¿Se llamará automáticamente al constructor de la clase base?
metodo constructor c# (6)
En c # utilizando clases base y derivadas, DEBE HABER ALGUNOS IMPLÍCITOS O EXPLÍCITOS LLAME A ALGUNOS CONSTRUCTORES DE LA CLASE BASE DE LA CLASE DERIVADA.
No entendí cómo funcionaba todo esto hasta que me di cuenta de ese hecho.
En otras palabras, cuando se conecta una clase base a una clase derivada, se debe llamar a algún constructor en la clase base desde la derivada. La clase base siempre se instancia primero de la clase derivada a través de una llamada a algún constructor en la clase base. A C # no le importa si es un constructor predeterminado o un constructor no predeterminado con parámetros. Es por eso que puede omitir un constructor predeterminado en todas sus clases, ya que se llama implícitamente SOLAMENTE SI no se agrega ningún otro no constructor con parámetro (s) en la clase base.
Cuando se agrega repentinamente un constructor no predeterminado con parámetros, se interrumpe la creación y las llamadas de la cadena del constructor predeterminado por defecto. En su clase Base con un constructor no predeterminado, ahora debe llamar a ese constructor explícitamente desde la clase derivada o agregar un constructor predeterminado explícitamente en la clase base.
Probemos esto .....
// THIS WORKS!!!
class MyBaseClass0
{
// no default constructor - created automatically for you
}
class DerivedClass0 : MyBaseClass0
{
// no default constructor - created automatically for you and calls the base class default constructor above
}
// THIS WORKS!!!
class MyBaseClass1
{
// same as above
}
class DerivedClass1 : MyBaseClass1
{
public DerivedClass1()
{
// here the derived class default constructor is created explicitly but the call to the base class default constructor is implicitly called
}
}
// AND THIS WORKS!!!
class MyBaseClass2
{
// as above
}
class DerivedClass2 : MyBaseClass2
{
public DerivedClass2() : base()
{
// here we explicitly call the default constructor in the base class using base(). note its optional as base constructor would be called anyway here
}
}
// AND THIS WORKS!!!
class MyBaseClass3
{
// no default constructor
}
class DerivedClass3 : MyBaseClass3
{
public DerivedClass3(int x)//non-default constructor
{
// as above, the default constructor in the base class is called behind the scenes implicitly here
}
}
// AND THIS WORKS
class MyBaseClass4
{
// non default constructor but missing default constructor
public MyBaseClass4(string y)
{
}
}
class DerivedClass4 : MyBaseClass4
{
// non default constructor but missing default constructor
public DerivedClass4(int x) : base("hello")
{
// note that here, we have fulfilled the requirement that some constructor be called in base even if its not default
}
}
// BUT THIS FAILS!!!...until you either add in a base() call to the non-default constructor or add in the default constructor into base!
class MyBaseClass5
{
// 1. EITHER ADD MISSING DEFAULT CONSTRUCTOR HERE AND CALL IT USING base() below....
public MyBaseClass5() { }
// 2. Or use the non-default constructor and call to base("hello") below
//public MyBaseClass5(string y)
//{
//}
}
class DerivedClass5 : MyBaseClass5
{
public DerivedClass5(int x) : base()// 1. Either ADD explicit call here to explicit default constructor in base class
{
}
//public DerivedClass5(int x) : base("hello")// 2. Or ADD explicit call here to parameter-based constructor in base class
//{
//}
}
El motivo por el que funcionan todos los elementos anteriores es: 1. La llamada al constructor predeterminado en la clase base se crea implícitamente en la clase base y se llama implícitamente desde el derivado porque no se ha agregado ningún constructor no predeterminado a la clase base o 2 Hay una llamada explícita a un constructor no predeterminado, basado en parámetros que usa base (myparamter)
- Lo que es confuso es cuándo y por qué los constructores predeterminados se crean en clases base y se llaman desde clases derivadas. Eso solo ocurre si NO aparecen constructores no predeterminados en la base.
class Person
{
public int age;
public Person()
{
age = 1;
}
}
class Customer : Person
{
public Customer()
{
age += 1;
}
}
Customer customer = new Customer();
¿La edad del cliente sería 2? Parece que el constructor de la clase base será llamado sin importar qué. Si es así, ¿por qué necesitamos llamar a la base
al final a veces?
public Customer() : base()
{
.............
}
Esto es simplemente cómo C # va a funcionar. Los constructores para cada tipo en la jerarquía de tipos serán llamados en el orden de Más Base -> Más Derivados.
Entonces, en su caso particular, llama a Person()
, y luego a Customer()
en las órdenes del constructor. La razón por la que a veces necesita usar el constructor base
es cuando los constructores debajo del tipo actual necesitan parámetros adicionales. Por ejemplo:
public class Base
{
public int SomeNumber { get; set; }
public Base(int someNumber)
{
SomeNumber = someNumber;
}
}
public class AlwaysThreeDerived : Base
{
public AlwaysThreeDerived()
: base(3)
{
}
}
Para construir un objeto AlwaysThreeDerived
, tiene un constructor sin parámetros. Sin embargo, el tipo Base
no lo hace. Por lo tanto, para crear un constructor sin parámetros, debe proporcionar un argumento al agente base, lo que puede hacer con la implementación base
.
No tengo mucho que agregar, pero he encontrado que debo llamar a MyConstructor (): base () sin parámetros en 1 caso. Tengo una clase base que implementa INotifyPropertyChanged de una manera en la que tengo una función virtual RegisterProperties (). Cuando lo sobrescribo, se llama en el constructor base. Así que termino teniendo que llamarlo en las subclases derivadas más recientes porque aparentemente se llamó a la base antes de que se reconociera el virtual anulado. Mis propiedades no notifican a menos que haga esto. La clase base completa está abajo.
Agregué una subclase de DatabaseTraits directamente debajo de ella. Sin la llamada de base () vacía, mis propiedades no llaman a OnPropertyChanged ().
[DataContract]
public abstract class DataModelBase : INotifyPropertyChanged, IDataErrorInfo {
#region Properties
[IgnoreDataMember]
public object Self {
get { return this; }
//only here to trigger change
set { OnPropertyChanged("Self"); }
}
#endregion Properties
#region Members
[IgnoreDataMember]
public Dispatcher Dispatcher { get; set; }
[DataMember]
private Dictionary<object, string> _properties = new Dictionary<object, string>();
#endregion Members
#region Initialization
public DataModelBase() {
if(Application.Current != null) Dispatcher = Application.Current.Dispatcher;
_properties.Clear();
RegisterProperties();
}
#endregion Initialization
#region Abstract Methods
/// <summary>
/// This method must be defined
/// </summar
protected abstract void RegisterProperties();
#endregion Abstract Methods
#region Behavior
protected virtual void OnPropertyChanged(string propertyName) {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool RegisterProperty<T>(ref T property, string propertyName) {
//causes problems in design mode
//if (property == null) throw new Exception("DataModelBase.RegisterProperty<T> : ref T property cannot be null.");
if (_properties.ContainsKey(property)) return false;
_properties.Add(property, propertyName);
return true;
}
protected string GetPropertyName<T>(ref T property) {
if (_properties.ContainsKey(property))
return _properties[property];
return string.Empty;
}
protected bool SetProperty<T>(ref T property, T value) {
//if (EqualityComparer<T>.Default.Equals(property, value)) return false;
property = value;
OnPropertyChanged(GetPropertyName(ref property));
OnPropertyChanged("Self");
return true;
}
[OnDeserialized]
public void AfterSerialization(StreamingContext context) {
if (Application.Current != null) Dispatcher = Application.Current.Dispatcher;
//---for some reason this member is not allocated after serialization
if (_properties == null) _properties = new Dictionary<object, string>();
_properties.Clear();
RegisterProperties();
}
#endregion Behavior
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion INotifyPropertyChanged Members
#region IDataErrorInfo Members
string IDataErrorInfo.Error {
get { throw new NotImplementedException(); }
}
string IDataErrorInfo.this[string propertyName] {
get { throw new NotImplementedException(); }
}
#endregion IDataErrorInfo Members
} //End class DataModelBaseclass DataModelBase
/*I decided to add an example subclass*/
[DataContract]
public abstract class DatabaseTraits : DataModelBase {
#region Properties
private long _id = -1;
[DataMember]
public long Id {
get { return _id; }
set { SetProperty(ref _id, value); }
}
private bool _isLocked = false;
[DataMember]
public bool IsLocked {
get { return _isLocked; }
set { SetProperty(ref _isLocked, value); }
}
private string _lockedBy = string.Empty;
[DataMember]
public string LockedBy {
get { return _lockedBy; }
set { SetProperty(ref _lockedBy, value); }
}
private DateTime _lockDate = new DateTime(0);
[DataMember]
public DateTime LockDate {
get { return _lockDate; }
set { SetProperty(ref _lockDate, value); }
}
private bool _isDeleted = false;
[DataMember]
public bool IsDeleted {
get { return _isDeleted; }
set { SetProperty(ref _isDeleted, value); }
}
#endregion Properties
#region Initialization
public DatabaseTraits() : base() {
/*makes sure my overriden RegisterProperties() is called.*/
}
protected override void RegisterProperties() {
RegisterProperty(ref _id, "Id");
RegisterProperty(ref _isLocked, "IsLocked");
RegisterProperty(ref _lockedBy, "LockedBy");
RegisterProperty(ref _lockDate, "LockDate");
RegisterProperty(ref _isDeleted, "IsDeleted");
}
#endregion Initialization
#region Methods
public void Copy(DatabaseTraits that) {
Id = that.Id;
IsLocked = that.IsLocked;
LockedBy = that.LockedBy;
LockDate = that.LockDate;
IsDeleted = that.IsDeleted;
}
#endregion Methods
}
Sí, el constructor de la clase base será llamado automáticamente. No es necesario agregar una llamada explícita a base()
cuando hay un constructor sin argumentos.
Puede probar esto fácilmente imprimiendo la edad del cliente después de la construcción ( enlace a ideone con una demostración ).
Se llama a base()
por defecto, pero se puede usar para otro propósito, como:
- El método base () `se usa para pasar el valor a la construcción de la clase padre o
- para llamar al constructor no-arg de la clase padre.
por ejemplo:
Caso 1: si el padre tiene un constructor parametrizado pero no es un constructor predeterminado o no arg.
class Person
{
private string FirstName;
private string LastName;
private string EmailAddress;
private DateTime DateOfBirth;
public Person(string firstName, string lastName, string emailAddress, DateTime dateOfBirth)
{
FirstName = firstName;
LastName = lastName;
EmailAddress = emailAddress;
DateOfBirth = dateOfBirth;
}
}
class Employee : Person
{
private double Salary { get; set; } = 0;
public Employee(string firstName, string lastName, string emailAddress, DateTime dateOfBirth,double salary)
:base(firstName,lastName,emailAddress,dateOfBirth)// used to pass value to parent constructor and it is mandatory if parent doesn''t have the no-argument constructor.
{
Salary = salary;
}
}
Caso 2: cuando el padre tiene más de un constructor junto con uno predeterminado.
class Person
{
private string FirstName;
private string LastName;
private string EmailAddress;
private DateTime DateOfBirth;
public Person()
{
// some important intialization''s to be done
}
public Person(string firstName, string lastName, string emailAddress, DateTime dateOfBirth)
{
FirstName = firstName;
LastName = lastName;
EmailAddress = emailAddress;
DateOfBirth = dateOfBirth;
}
}
class PermanentEmployee : Person
{
public double HRA { get; set; }
public double DA { get; set; }
public double Tax { get; set; }
public double NetPay { get; set; }
public double TotalPay { get; set; }
public PermanentEmployee(double hRA, double dA, double tax, double netPay, double totalPay) : base();
{
HRA = hRA;
DA = dA;
Tax = tax;
NetPay = netPay;
TotalPay = totalPay;
}
}
Aquí estamos llamando a un constructor sin argumentos manualmente por base () para realizar algunas intilizaciones pero no pasa ningún valor.
Espero que esto te ayudará.
Si no tuviera un constructor predeterminado sin parámetros, habría una necesidad de llamar al que tiene parámetros:
class Person
{
public Person(string random)
{
}
}
class Customer : Person
{
public Customer(string random) : base (random)
{
}
}