Здравствуйте, GrigorievAnton, Вы писали:
S>>Алгоритм в конструкторе? Нельзя ли вынести?
GA>Да, алгоритм зависит от параметров, которые переданы в конструктор. А дополнительная сущность окажется разная для разных классов, и пользователю библиотеки придётся следить за тем, чтобы подбирать нужную сущность к создаваемому объекту.
Им и так приходится "следить за тем" что бы правильно перекрыть метод GetParam. Предлагается заменить перегрузку метода и использование полей класса на написание нового метода и использование агрументов конструктора. Как-то так:
class BaseClass
{
// это будут вызывать наследники с алгоритмом вычисления param "по умолчанию"public BaseClass(x, y, z) : this(x, y, z, GetParam(x, y, z)) {
}
// это будут вызывать наследники с собственным алгоритмом вычисления paramprotected BaseClass(x, y, z, int param) {
}
private staticint 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.
Здравствуйте, GrigorievAnton, Вы писали:
GA>Задача такая: есть базовый класс и много наследников от него. В конструкторе всех классов нужно выполнить одинаковую последовательность действий, только с одним параметром, правила вычисления которого разные в различных классах. Я засунул эти вычисления в конструктор базового класса, а вычисление параметра вынес в отдельную виртуальную функцию, которую перекрываю в наследниках. Получается примерно так:
GA>
GA>class BaseClass
GA>{
GA> public BaseClass(...)
GA> {
GA> int param = GetParam(...);
GA> // Здесь реализация одинакового для всех классов алгоритма,
GA> // в котором есть зависимость от param
GA> }
Алгоритм в конструкторе? Нельзя ли вынести?
Если нет, можно вынести метод GetParam во вспомогательную сущность и передать ее в конструктор явно.
Здравствуйте, GrigorievAnton, Вы писали:
GA>Задача такая: есть базовый класс и много наследников от него. В конструкторе всех классов нужно выполнить одинаковую последовательность действий, только с одним параметром, правила вычисления которого разные в различных классах. Я засунул эти вычисления в конструктор базового класса, а вычисление параметра вынес в отдельную виртуальную функцию, которую перекрываю в наследниках. Получается примерно так:
Здравствуйте, 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.
Здравствуйте, GrigorievAnton, Вы писали:
GA>...Просто её сделали сложной именно в C#. GA>Ведь в Delphi же реализовано это так, что подобных проблем в принципе не бывает. GA>...
По-видимому, при проектировании C# (а это делали люди, не понаслышке знакомые с Delphi ), упор делался на максимальную безопасность (забыл проинициализировать локальную переменную — получи ошибку времени компиляции и т.п.). Скорее всего, эта особенность вызова базового конструктора проистекает оттуда же.
Правда, под давлением привыкшей к VB общественности эти позиции потихоньку сдаются (в последних версиях C# появились параметры по умолчанию, dynamic-типы и т.п.).
Задача такая: есть базовый класс и много наследников от него. В конструкторе всех классов нужно выполнить одинаковую последовательность действий, только с одним параметром, правила вычисления которого разные в различных классах. Я засунул эти вычисления в конструктор базового класса, а вычисление параметра вынес в отдельную виртуальную функцию, которую перекрываю в наследниках. Получается примерно так:
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# нельзя. Нет ли какого-то другого способа в базовом классе написать код так, чтобы он выполнялся сразу после того, как отработает код конструктора наследника?
. Но решения там, к сожалению, не предложено. Также поиск подсказал, что если поля инициализировать не в конструкторе, а непосредственно при объявлении, эта инициализация будет выполнена раньше вызова базового конструктора. Но мне это не помогает, т.к. поля в потомках нужно инициализировать не константами, а значениями, переданными в качестве параметра в конструкторе.
Здравствуйте, samius, Вы писали:
S>Алгоритм в конструкторе? Нельзя ли вынести?
Да, алгоритм зависит от параметров, которые переданы в конструктор. А дополнительная сущность окажется разная для разных классов, и пользователю библиотеки придётся следить за тем, чтобы подбирать нужную сущность к создаваемому объекту. Вообще, странно, что такая простая вещь не предусмотрена в языке. У меня всё-таки теплится надежда, что я просто какой-то хитрости не знаю.
Здравствуйте, GrigorievAnton, Вы писали:
GA>Здравствуйте, samius, Вы писали:
S>>Алгоритм в конструкторе? Нельзя ли вынести?
GA>Да, алгоритм зависит от параметров, которые переданы в конструктор. А дополнительная сущность окажется разная для разных классов, и пользователю библиотеки придётся следить за тем, чтобы подбирать нужную сущность к создаваемому объекту.
Эту сущность может определитьь производный объект, только вместо перекрытия метода, он сущность подсунет в конструктор базового класса. Беспокоиться придется только авторам производных классов.
Здравствуйте, GrigorievAnton, Вы писали:
GA>Здравствуйте, samius, Вы писали:
S>>Алгоритм в конструкторе? Нельзя ли вынести?
GA>Да, алгоритм зависит от параметров, которые переданы в конструктор. А дополнительная сущность окажется разная для разных классов, и пользователю библиотеки придётся следить за тем, чтобы подбирать нужную сущность к создаваемому объекту. Вообще, странно, что такая простая вещь не предусмотрена в языке. У меня всё-таки теплится надежда, что я просто какой-то хитрости не знаю.
Добавить метод Init и в этом методе звать виртуальную функцию. Звать виртуальные функции в конструкторе не желательно.
Спасибо, в таком ключе, видимо, и поступлю. Но согласиться с твоей оценкой, что это — сложная по своей сути вещь, не могу. Просто её сделали сложной именно в C#. Ведь в Delphi же реализовано это так, что подобных проблем в принципе не бывает. Там, правда, можно вообще забыть вызвать унаследованный конструктор, но таких проблем за 15 лет программирования я не помню Зато порядком инициализации управляю как хочу, и не надо по два конструктора и лишней функции писать
Здравствуйте, _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>Надо бы сначала посмотреть, как это сделано в нескольких языках, потом разобраться в том, почему сделано так, какие бенефиты и неудобства от этого есть, а потом уже осуждать ;о) было бы честней и интереснее.
Ну вот неудобства шарпа/С++ видны. А денефиты у них какие???
Здравствуйте, Jack128, Вы писали:
GA>>>Ведь в Delphi же реализовано это так, что подобных проблем в принципе не бывает. _FR>>В дельфи, как мне кажется, другая идеология: создание экземпляра и инициализация разделены. Тут тебе ничто не мешает сделать тоже самое: J>Для прикладного программиста создание и инициализация неотделимы (за исключением всяких хаков).
То есть в дельфи нет возможности вызвать "Create" самому у уже созданного объекта? Или создать "неинициализированный" объект — то есть объект, у которого не вызван Create?
_FR>>Надо бы сначала посмотреть, как это сделано в нескольких языках, потом разобраться в том, почему сделано так, какие бенефиты и неудобства от этого есть, а потом уже осуждать ;о) было бы честней и интереснее. J>Ну вот неудобства шарпа/С++ видны. А денефиты у них какие???
А вот мне не видны Я пока вижу примерно такой путь решения задачи: некие данные сначала передаются некоторому классу, тот передаёт их базе, база дёргает виртуальный метод наследника, который на основании тех же самых данных (которые переданы ему в конструкторе) возвращает данные для базы. Вне зависимости от способа создания объектов, это кривое решение.
Во второй цепочке нету возвращения назад, потому что оно не нужно.
Виртуальные методы имеет смысл использовать тогда, когда наследник в момент создания может не знать их поведения. В данном случае наследник обязан в момент создания знать, как сосчитать дополнительный параметр.
Help will always be given at Hogwarts to those who ask for it.
В Delphi есть еще AfterConstruction и BeforeDestruction и в System часто применяется.
Есть свобода выбора, но данные потребности встречается редко, поэтому для С# сделали ставку на безопасность и упрощении.
Хотя виртуальные методы внутри конструктора никто не отменял.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, _FRED_, Вы писали:
_FR>Здравствуйте, Jack128, Вы писали:
GA>>>>Ведь в Delphi же реализовано это так, что подобных проблем в принципе не бывает. _FR>>>В дельфи, как мне кажется, другая идеология: создание экземпляра и инициализация разделены. Тут тебе ничто не мешает сделать тоже самое: J>>Для прикладного программиста создание и инициализация неотделимы (за исключением всяких хаков).
_FR>То есть в дельфи нет возможности вызвать "Create" самому у уже созданного объекта? Или создать "неинициализированный" объект — то есть объект, у которого не вызван Create?
В принципе можно. Но за свои 7 лет программирования на дельфи я такой код видел только один раз в недрах VCL, там это для поддержки дейзанера сделано, AFAIK. такое написание — это даже хуже goto вс равно, что в private поля через рефлекшн лесть. Используется только в ОЧЕНЬ ограниченном числе сценариев и скорее всего среднестатистический дельфиец вообще не знает об этой возможности.
_FR>>>Надо бы сначала посмотреть, как это сделано в нескольких языках, потом разобраться в том, почему сделано так, какие бенефиты и неудобства от этого есть, а потом уже осуждать ;о) было бы честней и интереснее. J>>Ну вот неудобства шарпа/С++ видны. А денефиты у них какие???
_FR>А вот мне не видны Я пока вижу примерно такой путь решения задачи: некие данные сначала передаются некоторому классу, тот передаёт их базе, база дёргает виртуальный метод наследника, который на основании тех же самых данных (которые переданы ему в конструкторе) возвращает данные для базы. Вне зависимости от способа создания объектов, это кривое решение. _FR>
_FR>Во второй цепочке нету возвращения назад, потому что оно не нужно.
в решении 2 — GetParamMethod отвязан от SоmeClass. Код, который знает о SameClass должен еще знать о соответствующем этому классу методе GetParamMethod — это знание излишнее, не нужное.
В принципе да, можно создание объекта в фабричный метод обернуть, но тогда каждого наследника нуно будет свой метод писать.
_FR>Виртуальные методы имеет смысл использовать тогда, когда наследник в момент создания может не знать их поведения. В данном случае наследник обязан в момент создания знать, как сосчитать дополнительный параметр.
а в данном случае — наследник и не знает о своем поведении. Просто он реализует виртуальный метод предка. А то что этот метод еще и в конструкторе предка используется — это дело десятое.
Здравствуйте, andy1618, Вы писали:
A>По-видимому, при проектировании C# (а это делали люди, не понаслышке знакомые с Delphi ), упор делался на максимальную безопасность (забыл проинициализировать локальную переменную — получи ошибку времени компиляции и т.п.). Скорее всего, эта особенность вызова базового конструктора проистекает оттуда же.
При желании можно было бы сделать безопасно. Проанализировать, вызывается ли из конструктора конструктор предка — не более сложная задача, чем убедиться, что все пути функции вызывают return, а удобство от возможности вставить код до вызова конструктора очевидны.
Вообще, чем дальше в лес, тем больше неудобств я вижу. Например, мне нужно породить от некоторого класса наследника, в котором не будет никаких новых полей, а будет только переопределён один виртуальный метод, то, по идее, мне и новый конструктор не нужен. Между тем, за исключением тех случаев, когда достаточно конструктора по умолчанию, приходится всё равно объявлять новый конструктор, который всего лишь вызывает конструктор предка и больше ничего не делает. Из-за этого, например, определение нового класса исключений, которое в Delphi делается одной строчкой NewException=class(ParentException), в C# выливается в целую историю, так как для полноценной поддержки исключений в новом классе приходится объявлять целых четыре пустых конструктора. Если это не называется "перемудрили", то я уж и не знаю, какие ещё слова можно подобрать.
Здравствуйте, Jack128, Вы писали:
GA>>>>>Ведь в Delphi же реализовано это так, что подобных проблем в принципе не бывает. _FR>>>>В дельфи, как мне кажется, другая идеология: создание экземпляра и инициализация разделены. Тут тебе ничто не мешает сделать тоже самое: J>>>Для прикладного программиста создание и инициализация неотделимы (за исключением всяких хаков). _FR>>То есть в дельфи нет возможности вызвать "Create" самому у уже созданного объекта? Или создать "неинициализированный" объект — то есть объект, у которого не вызван Create? J>В принципе можно.
Вот поэтому и нужны IsInitialized и т.п. А reflection я могу запретить декларативно.
_FR>>Правильное решение — это из входных данных получить результат и использовать его для передачи в базу. _FR>>
_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.
Здравствуйте, _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;
для каждого класса в виртуальной таблице прописывается смещение определенных методов, и виртуальных методов класса (не объекта)
Здравствуйте, _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 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>Тебе это топикстартер сказал? В этом обсуждении атких слов небыло.
Ну это я скатился на вызов любых виртуальных методов в конструкторе, а не конкретной задаче Антона -)
Здравствуйте, 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.
Здравствуйте, GrigorievAnton, Вы писали:
GA>При желании можно было бы сделать безопасно. Проанализировать, вызывается ли из конструктора конструктор предка — не более сложная задача, чем убедиться, что все пути функции вызывают return, а удобство от возможности вставить код до вызова конструктора очевидны.
Кстати, в IL и, насколько я помню, в Nemerle такое возможно.
Здравствуйте, nikov, Вы писали:
GA>>При желании можно было бы сделать безопасно. Проанализировать, вызывается ли из конструктора конструктор предка — не более сложная задача, чем убедиться, что все пути функции вызывают return, а удобство от возможности вставить код до вызова конструктора очевидны.
N>Кстати, в IL и, насколько я помню, в Nemerle такое возможно.
И в VB.NET, кажется. но тогда все классы иерархии должны придерживаться некоторого соглашения о точке вызова конструктора предка, а кто это должен контролировать?
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>Здравствуйте, Jack128, Вы писали:
J>>Я пытался с мощью лямбд эту проблему решить:
_FR>В одной из версий фреймворка nikov обнаружил в подобном коде ошибку: компилиться всё компилилось, но верификацию не проходило. Потому что нечего пяткой к уху тянуться ;о)
да ладно, нормальный механизм был бы.
J>>непонял, кого нельзя объявить sealed ?? классы?
_FR>Тут я перепутал sealed и readonly. Если инициализацию полей проводить не в конструкторе, то полезная возможность иметь initonly поля идёт лесом.
А где инициализация проводится не в конструкторе? Все поля топик-стартера в конструкторе инициализируются.
Здравствуйте, _FRED_, Вы писали:
__FR>И в VB.NET, кажется. но тогда все классы иерархии должны придерживаться некоторого соглашения о точке вызова конструктора предка, а кто это должен контролировать?
Создатель класса. Иногда и ненужно его вызывать, так все действия может выполнить потомок.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, GrigorievAnton
Можно так извратиться
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var v = new Base();
var v2 = new Derived();
v = new Base(1,2,3);
v2 = new Derived(1, 2, 3);
Console.ReadKey();
}
}
class Base
{
protected virtual void AfterCreate()
{
Console.WriteLine("AfterCreate");
}
protected void Create(int x, int y, int z)
{
Console.WriteLine("Base");
Console.WriteLine("x = {0} y = {1} z = {2}", x, y, z);
}
protected void Create()
{
Console.WriteLine("Base без параметров");
}
protected virtual void InitializableCore(int x, int y, int z)
{
Create(x, y, z);
AfterCreate();
}
protected virtual void InitializableCore()
{
Create();
AfterCreate();
}
public Base(int x, int y, int z)
{
InitializableCore(x, y, z);
}
public Base()
{
InitializableCore();
}
}
class Derived : Base
{
protected override void InitializableCore(int x, int y, int z)
{
Create(x, y, z);
AfterCreate();
}
protected override void InitializableCore()
{
Create();
AfterCreate();
}
protected new void Create(int x, int y, int z)
{
Console.WriteLine("Derived");
Console.WriteLine("x = {0} y = {1} z = {2}", x, y, z);
base.Create(z, y, x);
}
protected new void Create()
{
Console.WriteLine("Derived без параметров");
base.Create();
}
public Derived()
: base()
{ }
public Derived(int x, int y, int z)
: base(x, y, z)
{ }
}
}
и солнце б утром не вставало, когда бы не было меня
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var v = new Base();
var v2 = new Derived();
v = new Base(1,2,3);
v2 = new Derived(1, 2, 3);
Console.ReadKey();
}
}
class Base
{
protected virtual void AfterCreate()
{
Console.WriteLine("AfterCreate");
Console.WriteLine("======================================");
}
protected virtual void Create(int x, int y, int z)
{
Console.WriteLine("Base");
Console.WriteLine("x = {0} y = {1} z = {2}", x, y, z);
}
protected virtual void Create()
{
Console.WriteLine("Base без параметров");
}
public Base(int x, int y, int z)
{
Create(x, y, z);
AfterCreate();
}
public Base()
{
Create();
AfterCreate();
}
}
class Derived : Base
{
protected override void Create(int x, int y, int z)
{
Console.WriteLine("Derived");
Console.WriteLine("x = {0} y = {1} z = {2}", x, y, z);
base.Create(z, y, x);
}
protected override void Create()
{
Console.WriteLine("Derived без параметров");
base.Create();
}
public Derived()
: base()
{ }
public Derived(int x, int y, int z)
: base(x, y, z)
{ }
}
}
и солнце б утром не вставало, когда бы не было меня