Как вызвать конструктор c
Перейти к содержимому

Как вызвать конструктор c

Конструкторы и наследование

В иерархии классов допускается, чтобы у базовых и производных классов были свои собственные конструкторы. В связи с этим возникает следующий резонный вопрос: какой конструктор отвечает за построение объекта производного класса: конструктор базового класса, конструктор производного класса или же оба? На этот вопрос можно ответить так: конструктор базового класса конструирует базовую часть объекта, а конструктор производного класса — производную часть этого объекта. И в этом есть своя логика, поскольку базовому классу неизвестны и недоступны любые элементы производного класса, а значит, их конструирование должно происходить раздельно.

Если конструктор определен только в производном классе, то все происходит очень просто: конструируется объект производного класса, а базовая часть объекта автоматически собирается его конструктором, используемым по умолчанию.

Когда конструкторы определяются как в базовом, так и в производном классе, процесс построения объекта несколько усложняется, поскольку должны выполняться конструкторы обоих классов. В данном случае приходится обращаться к ключевому слову base, которое находит двоякое применение: во-первых, для вызова конструктора базового класса; и во-вторых, для доступа к члену базового класса, скрывающегося за членом производного класса.

С помощью формы расширенного объявления конструктора производного класса и ключевого слова base в производном классе может быть вызван конструктор, определенный в его базовом классе. Ниже приведена общая форма этого расширенного объявления:

конструктор_производного_класса(список_параметров) : base (список_аргументов) < // тело конструктора >

где список_аргументов обозначает любые аргументы, необходимые конструктору в базовом классе. Обратите внимание на местоположение двоеточия.

Давайте рассмотрим пример:

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 < class MyClass < public int x, y, z; // Конструктор базового класса public MyClass(int x, int y, int z) < this.x = x; this.y = y; this.z = z; >> class ClassA : MyClass < int point; // Конструктор производного класса public ClassA(int point, int x, int y, int z) : base(x, y, z) < this.point = point; >public void Pointer(ClassA obj) < obj.x *= obj.point; obj.y *= obj.point; obj.z *= obj.point; Console.WriteLine("Новые координаты объекта:  ", obj.x, obj.y, obj.z); > > class Program < static void Main() < ClassA obj = new ClassA(10, 1, 4, 3); Console.WriteLine("Координаты объекта:  ", obj.x, obj.y, obj.z); obj.Pointer(obj); Console.ReadLine(); > > > 

Определение конструкторов при наследовании в C#

С помощью ключевого слова base можно вызвать конструктор любой формы, определяемой в базовом классе, причем выполняться будет лишь тот конструктор, параметры которого соответствуют переданным аргументам.

А теперь рассмотрим вкратце основные принципы действия ключевого слова base. Когда в производном классе указывается ключевое слово base, вызывается конструктор из его непосредственного базового класса. Следовательно, ключевое слово base всегда обращается к базовому классу, стоящему в иерархии непосредственно над вызывающим классом. Это справедливо даже для многоуровневой иерархии классов. Аргументы передаются базовому конструктору в качестве аргументов метода base(). Если же ключевое слово отсутствует, то автоматически вызывается конструктор, используемый в базовом классе по умолчанию.

Как вызвать конструктор с параметром, для класса являющегося членом другого класса.

Есть два класса c1 и c2 У каждого класса конструктор с параметром. Класс c1 является членом класса c2.

class c1< int v; public: c1(int n);// конструктор класса c1 >; c1::c1(int n) < v=n; printf("Constructior c1.\n"); >class c2< int v; public: c2(int n); // конструктор класса c2 c1 e1; >; c2::c2(int n) < v=n; e1.c1(n); //пытаюсь вызвать конструктор класса c1, но не выходит printf("Constructior c2\n"); >int main(int argc,char **argv)

При компиляции выдается сообщение о ошибке

test.cpp: In constructor ‘c2::c2(int)’: test.cpp:29: error: no matching function for call to ‘c1::c1()’ test.cpp:17: note: candidates are: c1::c1(int) test.cpp:12: note: c1::c1(const c1&) test.cpp:31: error: invalid use of ‘class c1’ 

Я так понимаю, что ошибка из за того, что конструктор вложенного класса имеет параметр. Вопрос. Как передать параметр конструктору члена класса?

Mixaluch384
04.11.11 23:08:48 MSK

Использование конструкторов (Руководство по программированию на C#)

При создании экземпляра класса или структуры вызывается его конструктор. Конструкторы имеют имя, совпадающее с именем класса или структуры, и обычно инициализируют члены данных нового объекта.

В следующем примере класс с именем Taxi определяется с помощью простого конструктора. Затем оператор new создает экземпляр этого класса. Конструктор Taxi вызывается оператором new сразу после того, как новому объекту будет выделена память.

public class Taxi < public bool IsInitialized; public Taxi() < IsInitialized = true; >> class TestTaxi < static void Main() < Taxi t = new Taxi(); Console.WriteLine(t.IsInitialized); >> 

Конструктор, который не принимает никаких параметров, называется конструктором без параметров. Конструкторы без параметров вызываются всякий раз, когда создается экземпляр объекта с помощью оператора new , а аргументы в new не передаются. В C# 12 представлены основные конструкторы. Основной конструктор задает параметры, которые необходимо предоставить для инициализации нового объекта. Дополнительные сведения см. в разделе Конструкторы экземпляров.

Если класс не является статическим, компилятор C# выделяет классам без конструкторов открытый конструктор без параметров, позволяющий создавать экземпляры классов. Дополнительные сведения см. в статье Статические классы и члены статических классов.

Создание экземпляров класса можно запретить, сделав конструктор закрытым, следующим образом:

class NLog < // Private Constructor: private NLog() < >public static double e = Math.E; //2.71828. > 

Дополнительные сведения см. в разделе Закрытые конструкторы.

Конструкторы для типов структур похожи на конструкторы классов. При создании экземпляра типа структуры с new помощью вызывается конструктор . Если для struct задано значение default , среда выполнения инициализирует всю память в структуре равным 0. До C# 10 не может содержать явный конструктор без параметров, structs так как он предоставляется компилятором автоматически. Дополнительные сведения см. в разделе инициализация структуры и значения по умолчанию статьи Типы структур .

В следующем коде используется конструктор без параметров для Int32, чтобы вы были уверены, что целое число инициализировано:

int i = new int(); Console.WriteLine(i); 

Однако следующий код вызывает ошибку компилятора, так как он не использует new и пытается использовать объект, который не был инициализирован:

int i; Console.WriteLine(i); 

Кроме того, объекты на основе structs (включая все встроенные числовые типы) можно инициализировать или назначить, а затем использовать, как в следующем примере:

int a = 44; // Initialize the value type. int b; b = 33; // Or assign it before using it. Console.WriteLine(", ", a, b); 

Как классы, так и структуры могут определять конструкторы, которые принимают параметры, включая первичные конструкторы. Конструкторы, принимающие параметры, необходимо вызывать с помощью оператора new или base. Классы и структуры также могут определять несколько конструкторов, и ни то, ни другое не требуется для определения конструктора без параметров. Пример:

public class Employee < public int Salary; public Employee() < >public Employee(int annualSalary) < Salary = annualSalary; >public Employee(int weeklySalary, int numberOfWeeks) < Salary = weeklySalary * numberOfWeeks; >> 

Этот класс можно создать, воспользовавшись одним из следующих операторов:

Employee e1 = new Employee(30000); Employee e2 = new Employee(500, 52); 

Конструктор может использовать ключевое слово base для вызова конструктора базового класса. Пример:

public class Manager : Employee < public Manager(int annualSalary) : base(annualSalary) < //Add further instructions here. >> 

В этом примере конструктор базового класса вызывается перед выполнением соответствующего ему блока. Ключевое слово base можно использовать как с параметрами, так и без них. Любые параметры для конструктора можно использовать как параметры для base или как часть выражения. Дополнительные сведения см. в разделе base.

В производном классе, если конструктор базового класса не вызывается явным образом с помощью base ключевое слово, конструктор без параметров, если он есть, вызывается неявно. Следующие объявления конструктора фактически одинаковы:

public Manager(int initialData) < //Add further instructions here. >
public Manager(int initialData) : base() < //Add further instructions here. >

Если базовый класс не предлагает конструктор без параметров, производный класс должен выполнить явный вызов базового конструктора с помощью base .

Конструктор может вызывать другой конструктор в том же объекте с помощью ключевого слова this. Как и base , this можно использовать с параметрами или без, а все параметры в конструкторе доступны как параметры this или как часть выражения. Например, второй конструктор в предыдущем примере можно переписать, используя this :

public Employee(int weeklySalary, int numberOfWeeks) : this(weeklySalary * numberOfWeeks)

Применение ключевого слова this в приведенном выше примере привело к вызову конструктора:

public Employee(int annualSalary)

Конструкторы могут иметь пометку public, private, protected, internal, protected internal или private protected. Эти модификаторы доступа определяют, каким образом пользователи класса смогут создавать класс. Дополнительные сведения см. в статье Модификаторы доступа.

Конструктор можно объявить статическим, используя ключевое слово static. Статические конструкторы вызываются автоматически непосредственно перед доступом к любым статическим полям и используются для инициализации членов статического класса. Дополнительные сведения см. в разделе Статические конструкторы.

Спецификация языка C#

Дополнительные сведения см. в разделах Конструкторы экземпляров и Статические конструкторы в Спецификации языка C#. Спецификация языка является предписывающим источником информации о синтаксисе и использовании языка C#.

См. также

  • Руководство по программированию на C#
  • Система типов C#
  • Конструкторы
  • Методы завершения

Совместная работа с нами на GitHub

Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.

The .NET documentation is open source. Provide feedback here.

Обратная связь

Отправить и просмотреть отзыв по

Вызываем конструктор базового типа в произвольном месте

Недавно проходил собеседование, и среди прочих был вопрос о порядке вызова конструкторов в C#. После ответа собеседующий решил продемонстрировать эрудицию и заявил, что вот в Java конструктор базового типа можно вызвать в любом месте конструктора производного типа, и C#, конечно, в этом проигрывает.

Утверждение оказалось ложью, враньем и провокацией

image

Но это уже не имело значения, потому что вызов был принят.

image

Дисклеймер

Приведенные ниже приемы не рекомендуется использовать в реальной жизни. Точнее даже рекомендуется не использовать. Это скорее тема для легкого светского разговора с коллегой. Или собеседующим.

Подготовка

Создаем цепочку наследования. Для простоты будем использовать конструкторы без параметров. В конструкторе будем выводить информацию о типе и идентификатор объекта, на котором он вызывается.

public class A < public A() < Console.WriteLine($"Type '' .ctor called on object #"); > > public class B : A < public B() < Console.WriteLine($"Type '' .ctor called on object #"); > > public class C : B < public C() < Console.WriteLine($"Type '' .ctor called on object #"); > > 
class Program < static void Main() < new C(); >> 

И получаем вывод:

Type ‘A’ .ctor called on object #58225482
Type ‘B’ .ctor called on object #58225482
Type ‘C’ .ctor called on object #58225482

Лирическое отступление

Перед выполнением конструктор может вызвать либо другой конструктор того же типа, либо любой доступный конструктор базового типа. Если вызов не указан явно, компилятор подставит вызов конструктора базового типа без параметров. Если базовый тип не предоставляет такой конструктор, происходит ошибка компиляции. При этом конструктор не может явно вызвать сам себя:

public A() : this() < >// CS0516 Constructor 'A.A()' cannot call itself 

и таким фокусом компилятор тоже не провести:

public A() : this(new object()) < >public A(object _) : this(0) < >public A(int _) : this() < >// CS0768 Constructor 'A.A(int)' cannot call itself through another constructor 
Удаление дублирующегося кода

Добавляем вспомогательный класс:

internal static class Extensions < public static void Trace(this object obj) =>Console.WriteLine($"Type '' .ctor called on object #"); > 

И заменяем во всех конструкторах

Console.WriteLine($"Type '' .ctor called on object #"); 
this.Trace(); 

Однако теперь программа выводит:

Type ‘C’ .ctor called on object #58225482
Type ‘C’ .ctor called on object #58225482
Type ‘C’ .ctor called on object #58225482

В нашем случае можно использовать следующую хитрость. Кто знает о типах времени компиляции? Компилятор. А еще он выбирает перегрузки методов на основе этих типов. И для обобщенных типов и методов генерирует сконструированные сущности тоже он. Поэтому возвращаем правильный вывод типов, переписав метод Trace следующим образом:

public static void Trace(this T obj) => Console.WriteLine($"Type '' .ctor called on object #"); 
Получение доступа к конструктору базового типа

Здесь на помощь приходит рефлексия. Добавляем в Extensions метод:

public static Action GetBaseConstructor(this T obj) => () => typeof(T) .BaseType .GetConstructor(Type.EmptyTypes) .Invoke(obj, Array.Empty()); 

В типы B и C добавляем свойство:

private Action @base => this.GetBaseConstructor(); 
Вызов конструктора базового типа в произвольном месте

Меняем содержимое конструкторов B и C на:

this.Trace(); @base(); 

Теперь вывод выглядит так:

Type ‘A’ .ctor called on object #58225482
Type ‘B’ .ctor called on object #58225482
Type ‘A’ .ctor called on object #58225482
Type ‘C’ .ctor called on object #58225482
Type ‘A’ .ctor called on object #58225482
Type ‘B’ .ctor called on object #58225482
Type ‘A’ .ctor called on object #58225482

Изменение порядка вызова конструкторов базового типа

Внутри типа A создаем вспомогательный тип:

protected class CtorHelper < private CtorHelper() < >> 

Так как здесь важна только семантика, конструктор типа целесообразно сделать закрытым. Создание экземпляров не имеет смысла. Тип предназначен исключительно для различения перегрузок конструкторов типа A и производных от него. По этой же причине тип следует разместить внутри A и сделать защищенным.

Добавляем в A, B и C соответствующие конструкторы:

protected A(CtorHelper _) < >protected B(CtorHelper _) < >protected C(CtorHelper _)

Для типов B и C ко всем конструкторам добавляем вызов:

: base(null) 

В результате классы должны выглядеть так

internal static class Extensions < public static Action GetBaseConstructor(this T obj) => () => typeof(T) .BaseType .GetConstructor(Type.EmptyTypes) .Invoke(obj, Array.Empty()); public static void Trace(this T obj) => Console.WriteLine($"Type '' .ctor called on object #"); > public class A < protected A(CtorHelper _) < >public A() < this.Trace(); >protected class CtorHelper < private CtorHelper() < >> > public class B : A < private Action @base =>this.GetBaseConstructor(); protected B(CtorHelper _) : base(null) < >public B() : base(null) < this.Trace(); @base(); >> public class C : B < private Action @base =>this.GetBaseConstructor(); protected C(CtorHelper _) : base(null) < >public C() : base(null) < this.Trace(); @base(); >> 

И вывод становится:

Type ‘C’ .ctor called on object #58225482
Type ‘B’ .ctor called on object #58225482
Type ‘A’ .ctor called on object #58225482

Наивный простачок думает, что обманул компилятор

Осмысление результата

Добавив в Extensions метод:

public static void TraceSurrogate(this T obj) => Console.WriteLine($"Type '' surrogate .ctor called on object #"); 

и вызвав его во всех конструкторах, принимающих CtorHelper, мы получим вывод:

Type ‘A’ surrogate .ctor called on object #58225482
Type ‘B’ surrogate .ctor called on object #58225482
Type ‘C’ .ctor called on object #58225482
Type ‘A’ surrogate .ctor called on object #58225482
Type ‘B’ .ctor called on object #58225482
Type ‘A’ .ctor called on object #58225482

Порядок следования конструкторов по принципу базовый/производный, конечно, не изменился. Но все же порядок следования доступных клиентскому коду конструкторов, несущих смысловую нагрузку, удалось поменять благодаря перенаправлению через вызовы недоступных клиенту ничего не делающих вспомогательных конструкторов.

  • ненормальное программирование
  • .net
  • c#
  • Ненормальное программирование
  • .NET
  • C#

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *