Re[6]: Исключение в конструкторе + финализатор
От: _FRED_ Черногория
Дата: 08.07.10 10:27
Оценка:
Здравствуйте, Mystic, Вы писали:

_FR>>Тут вот я не согласен: оченьо неожиданные спец-эффекты бывают Нафик-нафик такие пироги. Ничего хорошего, "рождённого" из этой фичи, я ещё не видел и представить даже не могу. Может, покажешь?


M>Например, в VCL при вызове конструктора формы в конце концов вызовется метод виртуальный CreateParams, в котором можно будет настроить параметры вызовов RegisterClass и CreateWindow.


А в FCL прекрасно обходится без этого Откуда необходимость вызывать CreateWindow непосредственно в конструкторе?
Help will always be given at Hogwarts to those who ask for it.
Re[9]: Исключение в конструкторе + финализатор
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 08.07.10 10:32
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>1. Создавать экземпляры абстрактных классов.

В Delphi нет абстрактных классов, есть только абстрактные методы.

0>2. Не вызывать конструктор/деструктор базового класса.

Это следствие того, что можно динамически вызывать любой конструктор/деструктор базового класса в зависимости от необходимости.

0>3. Подёргать методы базового класса ДО вызова его конструктора или ПОСЛЕ вызова его деструктора

См. п. 2


Вообще, если брать аналоги конструктора/деструктора в C++, то в Delphi это скорее всего методы NewInstance и FreeInstance. Именно они создают объект. А конструктор это уже метод, который инициализирует уже созданный объект. Поэтому конструктор может быть виртуальным и может дергать виртуальные методы.
Re[9]: Исключение в конструкторе + финализатор
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 08.07.10 10:39
Оценка:
Здравствуйте, _FRED_, Вы писали:

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


Был затеян автоматический вызов деструктора, в случае исключения в конструкторе. Это позволяет не думать о том, как освобождать объекты в конструкторе в случае, когда надо кинуть исключение (возникло исключение). Это очень удобная форма записи: в конструкторе только инициализация без всякого освобождения ресурсов в случае фатального завершения.
Re[5]: Исключение в конструкторе + финализатор
От: Lazy Cjow Rhrr Россия lj://_lcr_
Дата: 08.07.10 10:44
Оценка:
_FRED_,

LCR>>За возможность вызвать виртуальный метод в конструкторе — моя личная благодарность создателям и особенно тов. Хейлсбергу


_FR>Тут вот я не согласен: оченьо неожиданные спец-эффекты бывают Нафик-нафик такие пироги. Ничего хорошего, "рождённого" из этой фичи, я ещё не видел и представить даже не могу. Может, покажешь?


Да, конечно, без проблем.
public interface IScope
{
    void Wrap(Action action);
}

public class Parent
{
    protected readonly IScope Scope;
    public Parent()
    {
        Scope = CreateLocalScope();
    }

    protected virtual IScope CreateLocalScope()
    {
        return new SimpleScope();
    }

    public virtual void UseMe()
    {
        Scope.Wrap(() =>
        {
            // empty
        });
    }
}

public class Child1 : Parent
{
    protected override IScope CreateLocalScope()
    {
        return new TransactionalScope();
    }

    public override void UseMe()
    {
        Scope.Wrap(() =>
        {
            // the stuff in UseMe is inside a transaction
            // ...
        });
    }
}

public class Child2 : Parent
{
    protected override IScope CreateLocalScope()
    {
        return new RepeatedScope(3);
    }

    public override void UseMe()
    {
        Scope.Wrap(() =>
        {
            // the stuff in UseMe is repeated 3 times
            // and throw an exception in case of failure
            // ...
        });
    }
}

//------------------------------//    
var structure = new[]
{
    new Parent(),
    new Child1(),
    new Child2(),
};
foreach (var el in structure)
    el.UseMe();

В моём случае у меня были требования — класс должен иметь конструктор по-умолчанию. Кроме того, оказалось гораздо удобнее позволить классу-наследнику Parent самому конфигурить себя, вместо того чтобы городить ioc-stuff. Без виртуальности в конструкторе вызов new Child1() создавал бы класс, но он бы пребывал бы какое-то время в невалидном состоянии (пока ему, скажем, не сделают Scope=..), что вряд ли бы добавило радости впоследствии.
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[11]: Исключение в конструкторе + финализатор
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 08.07.10 10:44
Оценка:
Здравствуйте, _FRED_, Вы писали:

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


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


А мне нужен именно CreateFile. Мне надо устанавливать всякие флаги, начиная от SecurityAttributes и заканчивая FILE_FLAG_NO_BUFFERING, FILE_FLAG_OVERLAPPED и т. п. В конце концов это может быть любой другой вызов API.
Re[6]: Исключение в конструкторе + финализатор
От: Lazy Cjow Rhrr Россия lj://_lcr_
Дата: 08.07.10 10:48
Оценка:
LCR>..., что вряд ли бы добавило радости впоследствии.
"радости сопровождающему товарищу" имелось ввиду.
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[7]: Исключение в конструкторе + финализатор
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 08.07.10 10:51
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>А в FCL прекрасно обходится без этого Откуда необходимость вызывать CreateWindow непосредственно в конструкторе?


Удобно, когда объединены создание и инициализация Чем это плохо? Вполне возможный вариант.
Re[12]: Исключение в конструкторе + финализатор
От: _FRED_ Черногория
Дата: 08.07.10 11:22
Оценка:
Здравствуйте, Mystic, Вы писали:

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


M>А мне нужен именно CreateFile. Мне надо устанавливать всякие флаги, начиная от SecurityAttributes и заканчивая FILE_FLAG_NO_BUFFERING, FILE_FLAG_OVERLAPPED и т. п. В конце концов это может быть любой другой вызов API.


И что тебе мешает сначала сделать нормальную обёртку а потом уже нормально пользоваться ей вместо того что бы использовать как есть и говорить "как неудобно!"
Help will always be given at Hogwarts to those who ask for it.
Re[10]: Исключение в конструкторе + финализатор
От: _FRED_ Черногория
Дата: 08.07.10 11:28
Оценка:
Здравствуйте, Mystic, Вы писали:

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


M>Был затеян автоматический вызов деструктора, в случае исключения в конструкторе. Это позволяет не думать о том, как освобождать объекты в конструкторе в случае, когда надо кинуть исключение (возникло исключение). Это очень удобная форма записи: в конструкторе только инициализация без всякого освобождения ресурсов в случае фатального завершения.


В третий раз закинул дед невод: пруфлинк можно получить на то, где _разработчики_ языка (которые, собственно, и "затевали") говорят, что вызов дестуктора даже в случае возникновения ошибки в конструкторе обусловлен именно этими причинами?
Help will always be given at Hogwarts to those who ask for it.
Re[8]: Исключение в конструкторе + финализатор
От: _FRED_ Черногория
Дата: 08.07.10 11:30
Оценка:
Здравствуйте, Mystic, Вы писали:

_FR>>А в FCL прекрасно обходится без этого Откуда необходимость вызывать CreateWindow непосредственно в конструкторе?


M>Удобно, когда объединены создание и инициализация Чем это плохо? Вполне возможный вариант.


Тем, что в таком случае класс должен позволять все необходимые параметры передавать через конструктор, потому что иначе пользоваться таким классом без наследования будет нельзя. Если позволяет — то вызов виртуального метода не является необходимым. Если не позволяет — значит класс должен быть абстрактным (а в дельфях, как ты же и говорил, абстрактных классов нет ).

А тебе доподлинно известно, что CreateWindow происходит именно в конструкторе?
Help will always be given at Hogwarts to those who ask for it.
Re[6]: Исключение в конструкторе + финализатор
От: _FRED_ Черногория
Дата: 08.07.10 11:44
Оценка:
Здравствуйте, Lazy Cjow Rhrr, Вы писали:

LCR>>>За возможность вызвать виртуальный метод в конструкторе — моя личная благодарность создателям и особенно тов. Хейлсбергу


_FR>>Тут вот я не согласен: оченьо неожиданные спец-эффекты бывают Нафик-нафик такие пироги. Ничего хорошего, "рождённого" из этой фичи, я ещё не видел и представить даже не могу. Может, покажешь?


LCR>Да, конечно, без проблем.


И неужель это понятнее работает, чем такое вот:

public class Parent
{
    protected readonly IScope Scope;

    public Parent() : this(new SimpleScope()) { }

    protected Parent(IScope scope)
    {
        Scope = scope;
    }

    public virtual void UseMe()
    {
        Scope.Wrap(() =>
        {
            // empty
        });
    }
}

public class Child1 : Parent
{
    public Child1() : this(new TransactionalScope()) { }

    protected Child1(IScope scope) : base(scope) { }

    public override void UseMe()
    {
        Scope.Wrap(() =>
        {
            // the stuff in UseMe is inside a transaction
            // ...
        });
    }
}

public class Child2 : Parent
{
    public Child2() : this(new RepeatedScope(3)) { }

    protected Child2(IScope scope) : base(scope) { }

    public override void UseMe()
    {
        Scope.Wrap(() =>
        {
            // the stuff in UseMe is repeated 3 times
            // and throw an exception in case of failure
            // ...
        });
    }
}


LCR>В моём случае у меня были требования — класс должен иметь конструктор по-умолчанию.


Каждый класс и у меня имеет открытый конструктор по-умолчанию. Если требование распространялось и на не открытые конструкторы — то требования, мягко говоря, не традиционные.

LCR>Кроме того, оказалось гораздо удобнее позволить классу-наследнику Parent самому конфигурить себя, вместо того чтобы городить ioc-stuff.


Это что такое имеется в виду?

LCR>Без виртуальности в конструкторе вызов new Child1() создавал бы класс, но он бы пребывал бы какое-то время в невалидном состоянии (пока ему, скажем, не сделают Scope=..), что вряд ли бы добавило радости впоследствии.


Этого избежать удалось.
Help will always be given at Hogwarts to those who ask for it.
Re[9]: Исключение в конструкторе + финализатор
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 08.07.10 11:50
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Тем, что в таком случае класс должен позволять все необходимые параметры передавать через конструктор, потому что иначе пользоваться таким классом без наследования будет нельзя. Если позволяет — то вызов виртуального метода не является необходимым. Если не позволяет — значит класс должен быть абстрактным (а в дельфях, как ты же и говорил, абстрактных классов нет ).


Ну опять же, в 99% перекрывать CreateParams не надо. Но есть еще 1%, когда не сложно и перекрыть метод. Тем более, что на каждую форму автоматически создается новый класс, так что перекрыть CreateParams не так уж и сложно. Ну а новый компонент и есть новый класс.

_FR>А тебе доподлинно известно, что CreateWindow происходит именно в конструкторе?


По крайней мере, если перекрыть CreateParams, то при вызове конструктора она дергается. А уж конструктор там, или виртуальные методы AfterConstruction срабатывают я не в курсе Вообще конструктор формы лезет в ресурсы, читает оттуда значения, которые прописаны в DFM, устанавливает их, вызывает Loaded, регистрирует класс окна, создает окно. Там же дергается OnCreate всякие и т. п.
Re[13]: Исключение в конструкторе + финализатор
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 08.07.10 12:09
Оценка:
Здравствуйте, _FRED_, Вы писали:

M>>А мне нужен именно CreateFile. Мне надо устанавливать всякие флаги, начиная от SecurityAttributes и заканчивая FILE_FLAG_NO_BUFFERING, FILE_FLAG_OVERLAPPED и т. п. В конце концов это может быть любой другой вызов API.


_FR>И что тебе мешает сначала сделать нормальную обёртку а потом уже нормально пользоваться ей вместо того что бы использовать как есть и говорить "как неудобно!"


Собственно говоря, напрягает необходимость делать такую обвертку. Потом напрягает читать разбухший код (везде добавлено .handle или ->handle), где есть обвертки над известными и понятными, давно обсосанными API-методами, да еще, возможно, со своими хитрыми нюансами. Опять же, создание такой обвертки требует определенного уровня знания всяких нюансов, о которых бы хотелось не касаться вовсе. Например, вопросы копирования. Дъявол подталкивает написать что-то универсальное, откуда возникает сложности на пустом месте. Это же может послужить источником ошибки. Плюс при отладке надо будет смотреть внутрь обверточного класса класса, ибо сразу хрен разберешь что там. Или писать еще скрипты. Короче, лишний геморрой на ровном месте.

Вообще, тут намного проще и приятнее сделать конструктор, который бы инициализировал все нулями, потом сделать метод init, который бы мог нормально проинициализировать объект, потом сделать метод, который бы последовательно создал объект, а потом вызвал init, и вернул что получилось. А в случае исключения вызвал, как и полагается, деструктор. Или дергать init ручками. Не смертельно, конечно.
Re: Исключение в конструкторе + финализатор
От: TK Лес кывт.рф
Дата: 08.07.10 13:33
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Мне одному кажется неправильным, что при вылете исключения из конструктора класса для недосозданного объекта вызывается финализатор того же класса? Получается, что мы имеем нарушение инварианта — происходит вызов метода объекта, для которого не отработал конструктор.


Потому, что конструктор не занимается созданием объектов. Он занимается лишь их инициализацией. Никто не запрещает создавать экземпляры без вызова конструктора и "инициализировать" их как-то еще...

Т.е. никого нарушения инварианта нет — финализатор вызывается для созданных объектов. Вот, если во время создания объекта возникнет OutOfMemoryException или выключат свет то, в этом случае, финализатор вызван не будет.
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re[7]: Исключение в конструкторе + финализатор
От: Lazy Cjow Rhrr Россия lj://_lcr_
Дата: 09.07.10 06:30
Оценка:
_FRED_,

_FR>И неужель это понятнее работает, чем такое вот:


_FR>
_FR>public class Parent
_FR>{
_FR>    protected readonly IScope Scope;

_FR>    public Parent() : this(new SimpleScope()) { }

_FR>    protected Parent(IScope scope)
_FR>    {
_FR>        Scope = scope;
_FR>    }
_FR>}
_FR>


Какой ты хитрый, отрефакторил таки. Придётся доставать говномёт (то есть копипасту (слегка причёсанную) из реального гов кода):
public class ParentElement
{
    private int checksum;

    public Parent()
    {
        checksum = CelculateCheckSum();
    }

    protected int CalculateCheckSum()
    {
        int result = 0;
        foreach (IElement cur: Elements)
        {
            if (!IsExcluded(cur.tag()))
                result += cur.CalculateCheckSum();
        }
        return result % 256;
    }
    
    protected virtual IEnumerable<IElement> Elements { get { /*some defaults*/ } }
    protected virtual bool IsExcluded(Tag) { return false; }
}


Два:
public class ParentWorker
{
    public Parent()
    {
        PrepareWorkers();
    }

    protected virtual void PrepareWorkers()
    {
        if (GlobalQueue.Get() != null)
        {
            // do nothing in this case, but 
            // derived classes will do some things like
            // worker = new Thread("DerivedWorker");
        }
        GlobalQueue.Put(this, "I am created!");
    }
}


Три:
public class CachedParent
{
    public Parent(ArrayList bytes)
    {
        // this call requires that Equals and GetHashCode
        // are defined correctly!
        GlobalCache.Add(this);
    }
    public override bool Equals(object obj)
    {
        if (object.ReferenceEquals(this, obj))
            return true;
        // bla-bla-bla
        return true;
    }
    
    public override int GetHashCode()
    {
        return 0; // please, override me
    }
}

Жирным помечены вызовы, чувствительные к виртуальному вызову. Разумеется, workaround всегда есть, дублирование там, отдельный метод Init. В C++ народ же как-то обходится, живёт с этим и не жужжит (вроде бы).

LCR>>Кроме того, оказалось гораздо удобнее позволить классу-наследнику Parent самому конфигурить себя, вместо того чтобы городить ioc-stuff.

_FR>Это что такое имеется в виду?
Inversion-of-control-хрень
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[11]: Исключение в конструкторе + финализатор
От: Jack128  
Дата: 09.07.10 08:54
Оценка:
Здравствуйте, 0x7be, Вы писали:

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


0>При попытке динамически вызвать конструктор вылетит птич... исключение


В ран тайм... Ну так в ран тайм и в дельфи поднимается исключение. При вызове абстрактного метода..
Re[12]: Исключение в конструкторе + финализатор
От: 0x7be СССР  
Дата: 09.07.10 08:59
Оценка:
Здравствуйте, Jack128, Вы писали:

0>>При попытке динамически вызвать конструктор вылетит птич... исключение

J>В ран тайм... Ну так в ран тайм и в дельфи поднимается исключение. При вызове абстрактного метода..
Тут есть принципиальная разница. Обращения к абстрактному методу можно исключить на этапе компиляции. А попытку создать объект абстрактного класса через ссылку на метакласс — нельзя. Хотя, если подумать, то вполне можно
Re: Исключение в конструкторе + финализатор
От: rm822 Россия  
Дата: 16.07.10 22:00
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Коллеги!


0>Мне одному кажется неправильным, что при вылете исключения из конструктора класса для недосозданного объекта вызывается финализатор того же класса? Получается, что мы имеем нарушение инварианта — происходит вызов метода объекта, для которого не отработал конструктор.


в дотнете файналайз это просто метод вызываемый ГЦ — ну поленились разрабы, чего тут непонятного?
конечно можно(и нужно,я считаю) было сделать как ты хочешь
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.