Порядок вызова конструкторов в C#
От: GrigorievAnton  
Дата: 15.06.09 12:54
Оценка:
Задача такая: есть базовый класс и много наследников от него. В конструкторе всех классов нужно выполнить одинаковую последовательность действий, только с одним параметром, правила вычисления которого разные в различных классах. Я засунул эти вычисления в конструктор базового класса, а вычисление параметра вынес в отдельную виртуальную функцию, которую перекрываю в наследниках. Получается примерно так:


class BaseClass
{
 public BaseClass(...)
 {
   int param = GetParam(...);
   // Здесь реализация одинакового для всех классов алгоритма,
   // в котором есть зависимость от param
 }

 protected virtual int GetParam(...)
 {
   // Вычисляем параметр по алгоритму для BaseClass
 }
}

class AncestorClass : BaseClass
{
 public AncestorClass(...)
   : base(...)
 {
   // Инициализация полей AncestorClass
 }

 protected override int GetParam(...)
 {
   // Вычисляем параметры по алгоритму для AncestorClass
 }
}



Возникает следующая проблема: реализация GetParam для AncestorClass использует поля этолго класса, которые инициализируются в его конструкторе. Но эта инициализация выполняется после того, как будет вызван базовый конструктор, поэтому AncestorClass.GetParam возвращает неправильное значение. К сожалению, самому выбрать момент вызова унаследованного конструктора, как в Delphi, в C# нельзя. Нет ли какого-то другого способа в базовом классе написать код так, чтобы он выполнялся сразу после того, как отработает код конструктора наследника?

Нашёл обсуждение похожей темы здесь
Автор: Plutonia Experiment
Дата: 26.04.04
. Но решения там, к сожалению, не предложено. Также поиск подсказал, что если поля инициализировать не в конструкторе, а непосредственно при объявлении, эта инициализация будет выполнена раньше вызова базового конструктора. Но мне это не помогает, т.к. поля в потомках нужно инициализировать не константами, а значениями, переданными в качестве параметра в конструкторе.
Re: Порядок вызова конструкторов в C#
От: samius Япония http://sams-tricks.blogspot.com
Дата: 15.06.09 13:03
Оценка: +1
Здравствуйте, GrigorievAnton, Вы писали:

GA>Задача такая: есть базовый класс и много наследников от него. В конструкторе всех классов нужно выполнить одинаковую последовательность действий, только с одним параметром, правила вычисления которого разные в различных классах. Я засунул эти вычисления в конструктор базового класса, а вычисление параметра вынес в отдельную виртуальную функцию, которую перекрываю в наследниках. Получается примерно так:



GA>
GA>class BaseClass
GA>{
GA> public BaseClass(...)
GA> {
GA>   int param = GetParam(...);
GA>   // Здесь реализация одинакового для всех классов алгоритма,
GA>   // в котором есть зависимость от param
GA> }


Алгоритм в конструкторе? Нельзя ли вынести?

Если нет, можно вынести метод GetParam во вспомогательную сущность и передать ее в конструктор явно.
Re[2]: Порядок вызова конструкторов в C#
От: GrigorievAnton  
Дата: 16.06.09 05:25
Оценка:
Здравствуйте, samius, Вы писали:

S>Алгоритм в конструкторе? Нельзя ли вынести?


Да, алгоритм зависит от параметров, которые переданы в конструктор. А дополнительная сущность окажется разная для разных классов, и пользователю библиотеки придётся следить за тем, чтобы подбирать нужную сущность к создаваемому объекту. Вообще, странно, что такая простая вещь не предусмотрена в языке. У меня всё-таки теплится надежда, что я просто какой-то хитрости не знаю.
Re[3]: Порядок вызова конструкторов в C#
От: samius Япония http://sams-tricks.blogspot.com
Дата: 16.06.09 05:35
Оценка:
Здравствуйте, GrigorievAnton, Вы писали:

GA>Здравствуйте, samius, Вы писали:


S>>Алгоритм в конструкторе? Нельзя ли вынести?


GA>Да, алгоритм зависит от параметров, которые переданы в конструктор. А дополнительная сущность окажется разная для разных классов, и пользователю библиотеки придётся следить за тем, чтобы подбирать нужную сущность к создаваемому объекту.

Эту сущность может определитьь производный объект, только вместо перекрытия метода, он сущность подсунет в конструктор базового класса. Беспокоиться придется только авторам производных классов.
Re: Порядок вызова конструкторов в C#
От: nikov США http://www.linkedin.com/in/nikov
Дата: 16.06.09 05:36
Оценка: +1
Здравствуйте, GrigorievAnton, Вы писали:

GA>Задача такая: есть базовый класс и много наследников от него. В конструкторе всех классов нужно выполнить одинаковую последовательность действий, только с одним параметром, правила вычисления которого разные в различных классах. Я засунул эти вычисления в конструктор базового класса, а вычисление параметра вынес в отдельную виртуальную функцию, которую перекрываю в наследниках. Получается примерно так:


Попробуй переписать конструктор так:

public AncestorClass(...)
   : base(..., InitializeFields(...))
 {
 }

private ... InitializeFields(...)
{
  // Инициализация полей AncestorClass
}
Re[3]: Порядок вызова конструкторов в C#
От: anton_t Россия  
Дата: 16.06.09 05:37
Оценка:
Здравствуйте, GrigorievAnton, Вы писали:

GA>Здравствуйте, samius, Вы писали:


S>>Алгоритм в конструкторе? Нельзя ли вынести?


GA>Да, алгоритм зависит от параметров, которые переданы в конструктор. А дополнительная сущность окажется разная для разных классов, и пользователю библиотеки придётся следить за тем, чтобы подбирать нужную сущность к создаваемому объекту. Вообще, странно, что такая простая вещь не предусмотрена в языке. У меня всё-таки теплится надежда, что я просто какой-то хитрости не знаю.


Добавить метод Init и в этом методе звать виртуальную функцию. Звать виртуальные функции в конструкторе не желательно.
Re[3]: Порядок вызова конструкторов в C#
От: _FRED_ Черногория
Дата: 16.06.09 05:47
Оценка: 2 (1) +1
Здравствуйте, GrigorievAnton, Вы писали:

S>>Алгоритм в конструкторе? Нельзя ли вынести?


GA>Да, алгоритм зависит от параметров, которые переданы в конструктор. А дополнительная сущность окажется разная для разных классов, и пользователю библиотеки придётся следить за тем, чтобы подбирать нужную сущность к создаваемому объекту.


Им и так приходится "следить за тем" что бы правильно перекрыть метод GetParam. Предлагается заменить перегрузку метода и использование полей класса на написание нового метода и использование агрументов конструктора. Как-то так:

class BaseClass
{
  // это будут вызывать наследники с алгоритмом вычисления param "по умолчанию"
  public BaseClass(x, y, z) : this(x, y, z, GetParam(x, y, z)) {
  }

  // это будут вызывать наследники с собственным алгоритмом вычисления param
  protected BaseClass(x, y, z, int param) {
  }

  private static int GetParam(x, y, z) {
    // Вычисляем параметр по алгоритму для BaseClass
  }
}

class AncestorClass1 : BaseClass
{
  public AncestorClass(x, y, z) : base(x, y, z) // вычисление param "по-умолчанию"
  {
  }
}

class AncestorClass2 : BaseClass
{
  public AncestorClass(x, y, z) : base(x, y, z, GetParam(x, y, z)) // Собственный алгоритм
  {
    // Инициализация полей AncestorClass
  }

  private static int GetParam(x, y, z)
  {
    // Вычисляем параметры по алгоритму для AncestorClass
  }
}


GA>Вообще, странно, что такая простая вещь не предусмотрена в языке. У меня всё-таки теплится надежда, что я просто какой-то хитрости не знаю.


Это не простая вещь и она как раз предусмотрена, только не так, как это нужно тебе :о)) именно для того, что бы ты не наступил на грабли. В C++, к примеру, это ещё лучше предусмотрено — там вызов виртуального метода в конструкторе вообще не будет виртуальным.
Help will always be given at Hogwarts to those who ask for it.
Re[4]: Порядок вызова конструкторов в C#
От: GrigorievAnton  
Дата: 16.06.09 06:57
Оценка:
Спасибо, в таком ключе, видимо, и поступлю. Но согласиться с твоей оценкой, что это — сложная по своей сути вещь, не могу. Просто её сделали сложной именно в C#. Ведь в Delphi же реализовано это так, что подобных проблем в принципе не бывает. Там, правда, можно вообще забыть вызвать унаследованный конструктор, но таких проблем за 15 лет программирования я не помню Зато порядком инициализации управляю как хочу, и не надо по два конструктора и лишней функции писать
Re[5]: Порядок вызова конструкторов в C#
От: _FRED_ Черногория
Дата: 16.06.09 07:12
Оценка: +1
Здравствуйте, GrigorievAnton, Вы писали:

GA>…Но согласиться с твоей оценкой, что это — сложная по своей сути вещь, не могу. Просто её сделали сложной именно в C#.


Нет, я показал, как эту проблему "обошли" в С++.

GA>Ведь в Delphi же реализовано это так, что подобных проблем в принципе не бывает.


В дельфи, как мне кажется, другая идеология: создание экземпляра и инициализация разделены. Тут тебе ничто не мешает сделать тоже самое:
interface IInitializable
{
  void Initialize();
}

class Base : IInitializable
{
  protected virtual void InitializableCore() {
    // Инициализация по-умолчанию
  }

  public void Initialize() {
    InitializableCore();
  }
}

class Derived : Base
{
  protected override void InitializableCore() {
    // Инициализация конкретного класса
  }
}


Только вот придётся явно после создания объекта звать Initialize(), но это можно возложить на фабрику или вообще унаследовать Base от ContextBoundObject и не мучиться. Вот только для чего козе баян? В С# наоборот совместили создание объекта и его инициализацию в конструкторе. Иначе, надо было бы ещё иметь флаг IsInitialized и пляски вокруг него.

GA>Там, правда, можно вообще забыть вызвать унаследованный конструктор, но таких проблем за 15 лет программирования я не помню Зато порядком инициализации управляю как хочу, и не надо по два конструктора и лишней функции писать


Надо бы сначала посмотреть, как это сделано в нескольких языках, потом разобраться в том, почему сделано так, какие бенефиты и неудобства от этого есть, а потом уже осуждать ;о) было бы честней и интереснее.
Help will always be given at Hogwarts to those who ask for it.
Re[6]: Порядок вызова конструкторов в C#
От: Jack128  
Дата: 16.06.09 10:21
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Здравствуйте, GrigorievAnton, Вы писали:


GA>>…Но согласиться с твоей оценкой, что это — сложная по своей сути вещь, не могу. Просто её сделали сложной именно в C#.


_FR>Нет, я показал, как эту проблему "обошли" в С++.


GA>>Ведь в Delphi же реализовано это так, что подобных проблем в принципе не бывает.


_FR>В дельфи, как мне кажется, другая идеология: создание экземпляра и инициализация разделены. Тут тебе ничто не мешает сделать тоже самое:


Для прикладного программиста создание и инициализация неотделимы (за исключением всяких хаков).

MyObject := TMyObject.Create компилятором приобразуется примерно в такое:

TmpVar = AllocMem(TMyObject.InstanceSize); // выделение памяти под объект, все поля инициализированы нулями.
try
TmpVar.Create; // вызов тела конструктора.
except
TmpVar.Free; // уничтожение объекта, если в конструкторе исключение
raise; // рерайз исключения
end;


_FR>Только вот придётся явно после создания объекта звать Initialize(), но это можно возложить на фабрику или вообще унаследовать Base от ContextBoundObject и не мучиться. Вот только для чего козе баян? В С# наоборот совместили создание объекта и его инициализацию в конструкторе. Иначе, надо было бы ещё иметь флаг IsInitialized и пляски вокруг него.


в дельфи нету такого флага и вроде проблем нет.

GA>>Там, правда, можно вообще забыть вызвать унаследованный конструктор, но таких проблем за 15 лет программирования я не помню Зато порядком инициализации управляю как хочу, и не надо по два конструктора и лишней функции писать


_FR>Надо бы сначала посмотреть, как это сделано в нескольких языках, потом разобраться в том, почему сделано так, какие бенефиты и неудобства от этого есть, а потом уже осуждать ;о) было бы честней и интереснее.


Ну вот неудобства шарпа/С++ видны. А денефиты у них какие???
Re[7]: Порядок вызова конструкторов в C#
От: _FRED_ Черногория
Дата: 16.06.09 11:02
Оценка:
Здравствуйте, Jack128, Вы писали:

GA>>>Ведь в Delphi же реализовано это так, что подобных проблем в принципе не бывает.

_FR>>В дельфи, как мне кажется, другая идеология: создание экземпляра и инициализация разделены. Тут тебе ничто не мешает сделать тоже самое:
J>Для прикладного программиста создание и инициализация неотделимы (за исключением всяких хаков).

То есть в дельфи нет возможности вызвать "Create" самому у уже созданного объекта? Или создать "неинициализированный" объект — то есть объект, у которого не вызван Create?

_FR>>Надо бы сначала посмотреть, как это сделано в нескольких языках, потом разобраться в том, почему сделано так, какие бенефиты и неудобства от этого есть, а потом уже осуждать ;о) было бы честней и интереснее.

J>Ну вот неудобства шарпа/С++ видны. А денефиты у них какие???

А вот мне не видны Я пока вижу примерно такой путь решения задачи: некие данные сначала передаются некоторому классу, тот передаёт их базе, база дёргает виртуальный метод наследника, который на основании тех же самых данных (которые переданы ему в конструкторе) возвращает данные для базы. Вне зависимости от способа создания объектов, это кривое решение.
(данные)=>[SomeClass]=>(данные)=>[BaseClass]=>(данные)=>[GetParamMethod]=>(данные)=>[SomeClass]


Правильное решение — это из входных данных получить результат и использовать его для передачи в базу.
(данные)=>[GetParamMethod]=>(данные2)=>[SomeClass]=>(данные2)=>[BaseClass]

Во второй цепочке нету возвращения назад, потому что оно не нужно.

Виртуальные методы имеет смысл использовать тогда, когда наследник в момент создания может не знать их поведения. В данном случае наследник обязан в момент создания знать, как сосчитать дополнительный параметр.
Help will always be given at Hogwarts to those who ask for it.
Re[5]: Порядок вызова конструкторов в C#
От: andy1618 Россия  
Дата: 16.06.09 11:14
Оценка: -1
Здравствуйте, GrigorievAnton, Вы писали:

GA>...Просто её сделали сложной именно в C#.

GA>Ведь в Delphi же реализовано это так, что подобных проблем в принципе не бывает.
GA>...

По-видимому, при проектировании C# (а это делали люди, не понаслышке знакомые с Delphi ), упор делался на максимальную безопасность (забыл проинициализировать локальную переменную — получи ошибку времени компиляции и т.п.). Скорее всего, эта особенность вызова базового конструктора проистекает оттуда же.
Правда, под давлением привыкшей к VB общественности эти позиции потихоньку сдаются (в последних версиях C# появились параметры по умолчанию, dynamic-типы и т.п.).
Re[8]: Порядок вызова конструкторов в C#
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 16.06.09 11:50
Оценка:
Здравствуйте, _FRED_, Вы писали:

В Delphi есть еще AfterConstruction и BeforeDestruction и в System часто применяется.
Есть свобода выбора, но данные потребности встречается редко, поэтому для С# сделали ставку на безопасность и упрощении.
Хотя виртуальные методы внутри конструктора никто не отменял.
и солнце б утром не вставало, когда бы не было меня
Re[8]: Порядок вызова конструкторов в C#
От: Jack128  
Дата: 16.06.09 11:58
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Здравствуйте, Jack128, Вы писали:


GA>>>>Ведь в Delphi же реализовано это так, что подобных проблем в принципе не бывает.

_FR>>>В дельфи, как мне кажется, другая идеология: создание экземпляра и инициализация разделены. Тут тебе ничто не мешает сделать тоже самое:
J>>Для прикладного программиста создание и инициализация неотделимы (за исключением всяких хаков).

_FR>То есть в дельфи нет возможности вызвать "Create" самому у уже созданного объекта? Или создать "неинициализированный" объект — то есть объект, у которого не вызван Create?

В принципе можно. Но за свои 7 лет программирования на дельфи я такой код видел только один раз в недрах VCL, там это для поддержки дейзанера сделано, AFAIK. такое написание — это даже хуже goto вс равно, что в private поля через рефлекшн лесть. Используется только в ОЧЕНЬ ограниченном числе сценариев и скорее всего среднестатистический дельфиец вообще не знает об этой возможности.

_FR>>>Надо бы сначала посмотреть, как это сделано в нескольких языках, потом разобраться в том, почему сделано так, какие бенефиты и неудобства от этого есть, а потом уже осуждать ;о) было бы честней и интереснее.

J>>Ну вот неудобства шарпа/С++ видны. А денефиты у них какие???

_FR>А вот мне не видны Я пока вижу примерно такой путь решения задачи: некие данные сначала передаются некоторому классу, тот передаёт их базе, база дёргает виртуальный метод наследника, который на основании тех же самых данных (которые переданы ему в конструкторе) возвращает данные для базы. Вне зависимости от способа создания объектов, это кривое решение.

_FR>
(данные)=>[SomeClass]=>(данные)=>[BaseClass]=>(данные)=>[GetParamMethod]=>(данные)=>[SomeClass]


_FR>Правильное решение — это из входных данных получить результат и использовать его для передачи в базу.

_FR>
(данные)=>[GetParamMethod]=>(данные2)=>[SomeClass]=>(данные2)=>[BaseClass]

_FR>Во второй цепочке нету возвращения назад, потому что оно не нужно.

в решении 2 — GetParamMethod отвязан от SоmeClass. Код, который знает о SameClass должен еще знать о соответствующем этому классу методе GetParamMethod — это знание излишнее, не нужное.

В принципе да, можно создание объекта в фабричный метод обернуть, но тогда каждого наследника нуно будет свой метод писать.


_FR>Виртуальные методы имеет смысл использовать тогда, когда наследник в момент создания может не знать их поведения. В данном случае наследник обязан в момент создания знать, как сосчитать дополнительный параметр.

а в данном случае — наследник и не знает о своем поведении. Просто он реализует виртуальный метод предка. А то что этот метод еще и в конструкторе предка используется — это дело десятое.
Re[6]: Порядок вызова конструкторов в C#
От: GrigorievAnton  
Дата: 16.06.09 12:29
Оценка:
Здравствуйте, andy1618, Вы писали:

A>По-видимому, при проектировании C# (а это делали люди, не понаслышке знакомые с Delphi ), упор делался на максимальную безопасность (забыл проинициализировать локальную переменную — получи ошибку времени компиляции и т.п.). Скорее всего, эта особенность вызова базового конструктора проистекает оттуда же.


При желании можно было бы сделать безопасно. Проанализировать, вызывается ли из конструктора конструктор предка — не более сложная задача, чем убедиться, что все пути функции вызывают return, а удобство от возможности вставить код до вызова конструктора очевидны.

Вообще, чем дальше в лес, тем больше неудобств я вижу. Например, мне нужно породить от некоторого класса наследника, в котором не будет никаких новых полей, а будет только переопределён один виртуальный метод, то, по идее, мне и новый конструктор не нужен. Между тем, за исключением тех случаев, когда достаточно конструктора по умолчанию, приходится всё равно объявлять новый конструктор, который всего лишь вызывает конструктор предка и больше ничего не делает. Из-за этого, например, определение нового класса исключений, которое в Delphi делается одной строчкой NewException=class(ParentException), в C# выливается в целую историю, так как для полноценной поддержки исключений в новом классе приходится объявлять целых четыре пустых конструктора. Если это не называется "перемудрили", то я уж и не знаю, какие ещё слова можно подобрать.
Re[9]: Порядок вызова конструкторов в C#
От: _FRED_ Черногория
Дата: 16.06.09 12:44
Оценка:
Здравствуйте, Jack128, Вы писали:

GA>>>>>Ведь в Delphi же реализовано это так, что подобных проблем в принципе не бывает.

_FR>>>>В дельфи, как мне кажется, другая идеология: создание экземпляра и инициализация разделены. Тут тебе ничто не мешает сделать тоже самое:
J>>>Для прикладного программиста создание и инициализация неотделимы (за исключением всяких хаков).
_FR>>То есть в дельфи нет возможности вызвать "Create" самому у уже созданного объекта? Или создать "неинициализированный" объект — то есть объект, у которого не вызван Create?
J>В принципе можно.

Вот поэтому и нужны IsInitialized и т.п. А reflection я могу запретить декларативно.

_FR>>Правильное решение — это из входных данных получить результат и использовать его для передачи в базу.

_FR>>
(данные)=>[GetParamMethod]=>(данные2)=>[SomeClass]=>(данные2)=>[BaseClass]

_FR>>Во второй цепочке нету возвращения назад, потому что оно не нужно.

J>в решении 2 — GetParamMethod отвязан от SоmeClass.


Правильно, отвязан. А зачем ему знать про SоmeClass, если для него определяющими являются "(данные)"?

J>Код, который знает о SameClass должен еще знать о соответствующем этому классу методе GetParamMethod — это знание излишнее, не нужное.


Не должен. Вот повторю код:
class AncestorClass2 : BaseClass
{
  public AncestorClass(x, y, z) : base(x, y, z, GetParam(x, y, z)) // Собственный алгоритм
  {
    // Инициализация полей AncestorClass
  }

  private static int GetParam(x, y, z)
  {
    // Вычисляем параметры по алгоритму для AncestorClass
  }
}

GetParamMethod используется внутри SomeClass, то есть SomeClass знает о GetParamMethod, и никто другой не знает.

J>В принципе да, можно создание объекта в фабричный метод обернуть, но тогда каждого наследника нуно будет свой метод писать.


Точно так же как в дельфях для каждого наследника пишется Create. А конструкторы просто н используй — тогда ничем отличаться не будет. Я кстати, так и не получил ответа на то, как выглядит вызов конструктора с параметрами в дельфях. Подозреваю, что точно так же — парамерты передаются в Create, откуда следует невозможность объявить их sealed и прочее.

_FR>>Виртуальные методы имеет смысл использовать тогда, когда наследник в момент создания может не знать их поведения. В данном случае наследник обязан в момент создания знать, как сосчитать дополнительный параметр.

J>а в данном случае — наследник и не знает о своем поведении. Просто он реализует виртуальный метод предка. А то что этот метод еще и в конструкторе предка используется — это дело десятое.

Тебе это топикстартер сказал? В этом обсуждении атких слов небыло.
Help will always be given at Hogwarts to those who ask for it.
Re[10]: Порядок вызова конструкторов в C#
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 16.06.09 12:59
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Точно так же как в дельфях для каждого наследника пишется Create. А конструкторы просто н используй — тогда ничем отличаться не будет. Я кстати, так и не получил ответа на то, как выглядит вызов конструктора с параметрами в дельфях. Подозреваю, что точно так же — парамерты передаются в Create, откуда следует невозможность объявить их sealed и прочее.



function _ClassCreate(AClass: TClass; Alloc: Boolean): TObject;
asm
        { ->    EAX = pointer to VMT      }
        { <-    EAX = pointer to instance }
        PUSH    EDX
        PUSH    ECX
        PUSH    EBX
        TEST    DL,DL
        JL      @@noAlloc
        CALL    DWORD PTR [EAX] + VMTOFFSET TObject.NewInstance
@@noAlloc:
{$IFNDEF PC_MAPPED_EXCEPTIONS}
        XOR     EDX,EDX
        LEA     ECX,[ESP+16]
        MOV     EBX,FS:[EDX]
        MOV     [ECX].TExcFrame.next,EBX
        MOV     [ECX].TExcFrame.hEBP,EBP
        MOV     [ECX].TExcFrame.desc,offset @desc
        MOV     [ECX].TexcFrame.ConstructedObject,EAX   { trick: remember copy to instance }
        MOV     FS:[EDX],ECX
{$ENDIF}
        POP     EBX
        POP     ECX
        POP     EDX
        RET

{$IFNDEF PC_MAPPED_EXCEPTIONS}
@desc:
        JMP     _HandleAnyException

  {       destroy the object                                                      }

        MOV     EAX,[ESP+8+9*4]
        MOV     EAX,[EAX].TExcFrame.ConstructedObject
        TEST    EAX,EAX
        JE      @@skip
        MOV     ECX,[EAX]
        MOV     DL,$81
        PUSH    EAX
        CALL    DWORD PTR [ECX] + VMTOFFSET TObject.Destroy
        POP     EAX
        CALL    _ClassDestroy
@@skip:
  {       reraise the exception   }
        CALL    _RaiseAgain
{$ENDIF}
end;




class function TObject.NewInstance: TObject;
begin
  Result := InitInstance(_GetMem(InstanceSize));
end;


для каждого класса в виртуальной таблице прописывается смещение определенных методов, и виртуальных методов класса (не объекта)


{ Virtual method table entries }

  vmtSelfPtr           = -76;
  vmtIntfTable         = -72;
  vmtAutoTable         = -68;
  vmtInitTable         = -64;
  vmtTypeInfo          = -60;
  vmtFieldTable        = -56;
  vmtMethodTable       = -52;
  vmtDynamicTable      = -48;
  vmtClassName         = -44;
  vmtInstanceSize      = -40;
  vmtParent            = -36;
  vmtSafeCallException = -32 deprecated;  // don't use these constants.
  vmtAfterConstruction = -28 deprecated;  // use VMTOFFSET in asm code instead
  vmtBeforeDestruction = -24 deprecated;
  vmtDispatch          = -20 deprecated;
  vmtDefaultHandler    = -16 deprecated;
  vmtNewInstance       = -12 deprecated;
  vmtFreeInstance      = -8 deprecated;
  vmtDestroy           = -4 deprecated;

  vmtQueryInterface    = 0 deprecated;
  vmtAddRef            = 4 deprecated;
  vmtRelease           = 8 deprecated;
  vmtCreateObject      = 12 deprecated;
и солнце б утром не вставало, когда бы не было меня
Re[10]: Порядок вызова конструкторов в C#
От: Jack128  
Дата: 16.06.09 13:07
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Здравствуйте, Jack128, Вы писали:



J>>В принципе можно.


_FR>Вот поэтому и нужны IsInitialized и т.п. А reflection я могу запретить декларативно.

Ну если можешь запретить — это хорошо -) Насчет принципиальной возможности вызвать Create как метод, я задовал этот вопрос в борлондовской конфе, они так внятно и не смогли сделать почему такая возможность просочилась в язык.

J>>в решении 2 — GetParamMethod отвязан от SоmeClass.


_FR>Правильно, отвязан. А зачем ему знать про SоmeClass, если для него определяющими являются "(данные)"?


J>>Код, который знает о SameClass должен еще знать о соответствующем этому классу методе GetParamMethod — это знание излишнее, не нужное.


_FR>Не должен. Вот повторю код:

_FR>
_FR>class AncestorClass2 : BaseClass
_FR>{
_FR>  public AncestorClass(x, y, z) : base(x, y, z, GetParam(x, y, z)) // Собственный алгоритм
_FR>  {
_FR>    // Инициализация полей AncestorClass
_FR>  }

_FR>  private static int GetParam(x, y, z)
_FR>  {
_FR>    // Вычисляем параметры по алгоритму для AncestorClass
_FR>  }
_FR>}
_FR>

_FR>GetParamMethod используется внутри SomeClass, то есть SomeClass знает о GetParamMethod, и никто другой не знает.

Дык в этом коде GetParam не имеет доступа к самому объекту. весьма сильное ограничение. Я пытался с мощью лямбд эту проблему решить:


public class Class1
{
public Class1() : this(() => 10) { }
public int SomeProp { get; set; }

public int Param { get { return _param; } }

protected Class1(Func<int> getParam)
{
SomeProp = 10;
_param = getParam();
}
int _param;
}

public class Class2 : Class1
{
public Class2(int param) : base(() => param + this.SomeProp) { } // но вот тут this == null
}

J>>В принципе да, можно создание объекта в фабричный метод обернуть, но тогда каждого наследника нуно будет свой метод писать.


_FR>Точно так же как в дельфях для каждого наследника пишется Create. А конструкторы просто н используй — тогда ничем отличаться не будет. Я кстати, так и не получил ответа на то, как выглядит вызов конструктора с параметрами в дельфях. Подозреваю, что точно так же — парамерты передаются в Create, откуда следует невозможность объявить их sealed и прочее.


ну да, конечно. MyObj = TMyObj.Create(Param1, Param2, ...);
непонял, кого нельзя объявить sealed ?? классы? Не в дельфи их нельзя объявить sealed из-за комбинации классов ссылок/виртуальных конструктов. Точнее в новых версиях ключевое слово такое появилось, но реально оно не гарантирует несозможность создания экземпляров классов.

_FR>>>Виртуальные методы имеет смысл использовать тогда, когда наследник в момент создания может не знать их поведения. В данном случае наследник обязан в момент создания знать, как сосчитать дополнительный параметр.

J>>а в данном случае — наследник и не знает о своем поведении. Просто он реализует виртуальный метод предка. А то что этот метод еще и в конструкторе предка используется — это дело десятое.

_FR>Тебе это топикстартер сказал? В этом обсуждении атких слов небыло.

Ну это я скатился на вызов любых виртуальных методов в конструкторе, а не конкретной задаче Антона -)
Re[11]: Порядок вызова конструкторов в C#
От: _FRED_ Черногория
Дата: 16.06.09 13:22
Оценка:
Здравствуйте, Jack128, Вы писали:

J>Дык в этом коде GetParam не имеет доступа к самому объекту. весьма сильное ограничение.


Это не ни какое не ограничение. Это то, что нужно топикстартеру.

J>Я пытался с мощью лямбд эту проблему решить:


В одной из версий фреймворка nikov обнаружил в подобном коде ошибку: компилиться всё компилилось, но верификацию не проходило. Потому что нечего пяткой к уху тянуться ;о)

J>непонял, кого нельзя объявить sealed ?? классы?


Тут я перепутал sealed и readonly. Если инициализацию полей проводить не в конструкторе, то полезная возможность иметь initonly поля идёт лесом.

_FR>>>>Виртуальные методы имеет смысл использовать тогда, когда наследник в момент создания может не знать их поведения. В данном случае наследник обязан в момент создания знать, как сосчитать дополнительный параметр.

J>>>а в данном случае — наследник и не знает о своем поведении. Просто он реализует виртуальный метод предка. А то что этот метод еще и в конструкторе предка используется — это дело десятое.
Help will always be given at Hogwarts to those who ask for it.
Re[7]: Порядок вызова конструкторов в C#
От: nikov США http://www.linkedin.com/in/nikov
Дата: 16.06.09 13:30
Оценка:
Здравствуйте, GrigorievAnton, Вы писали:

GA>При желании можно было бы сделать безопасно. Проанализировать, вызывается ли из конструктора конструктор предка — не более сложная задача, чем убедиться, что все пути функции вызывают return, а удобство от возможности вставить код до вызова конструктора очевидны.


Кстати, в IL и, насколько я помню, в Nemerle такое возможно.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.