| Comments: |
>> как разработчики добились такого поведения?
вроде, генерируется класс, время жизни которого совпадает с временем жизни к(л)ожуры, в него пихаются полями все переменные, попадающие в замыкание и заменяются все ссылки на них - ссылками на переменные этого экземпляра.
то есть, на самом деле они из стека переезжают в хип.
если внимательно читать стандарт C#, то [b]нигде не сказано, что локальные переменные выделяются на стеке[/b]. они там _могут_ выделяться как оптимизация. но вообще value semantics означает именно что они by value, и никоим образом не "форсирует" то, что они в стеке.
поэтому в этом случае остальное поведение переменных остается полностью согласно стандарту и всё хорошо
а вот как оно себя ведет при re-entrancy и потоках - не знаю. не хочу думать :)
![[User Picture]](http://l-userpic.livejournal.com/92895047/349817) | From: 109 2009-05-21 08:48 am (UTC)
| (Link)
|
а вот как оно себя ведет при re-entrancy и потоках - не знаю. не хочу думать :)
так а что там думать - это инстанс анонимного класса, так себя и ведёт.
ну инстанс это реализация, мало ли, вдруг они какую семантику специальную имеют
>нигде не сказано, что локальные переменные выделяются на стеке
Дело не в том, локальные они или нет, а в том, value-тип или reference-тип. Стандарт Ecma на CLI и C# я детально не изучал, но Рихтер говорит, что переменная value-типа размещается или на стеке, или в куче, если она входит в состав reference-типа. (Кроме того, в стандарте на CLI стек весьма активно фигурирует.)
Так вот, в этом примере создаётся обманчивое впечатление, что переменная val-типа размещается на стеке. На самом же деле, она размещается в куче в составе класса, неявно созданного под замыкание.
ну то есть строго говоря в сишарпе не подразумевается даже существование стэка - return ведь можно сделать и без него
это implementation details, которое, правда, влияет на вызов диспоз и финализаторов у IDisposable обьектов, обьявленных локально.
кстати!!!
надо сделать переменной stackalloc и запихать ее в кложуру, и посмотреть :-D
![[User Picture]](http://l-userpic.livejournal.com/92895047/349817) | From: 109 2009-05-21 08:49 am (UTC)
| (Link)
|
а поподробнее про диспоз и финализаторы можно?
>а поподробнее про диспоз и финализаторы можно?
Наверное, имеется в виду, что если лямбда захватывает переменную, скоуп которой вводится конструкцией using, то всё будет плохо.
![[User Picture]](http://l-userpic.livejournal.com/92895047/349817) | From: 109 2009-05-21 09:08 am (UTC)
| (Link)
|
причём здесь лямбда, вообще? я вот про это спрашиваю: [stack] - это implementation details, которое, правда, влияет на вызов диспоз и финализаторов у IDisposable обьектов, обьявленных локально.
Согласен, фраза некорректная.
![[User Picture]](http://l-userpic.livejournal.com/92895047/349817) | From: 109 2009-05-21 09:39 am (UTC)
| (Link)
|
не, я вовсе не говорю, что некорректная. я лично не знаю ничего про упомянутые детали имплементации, но мало ли чего я не знаю. вот я и попросил поподробнее.
Мне кажется, или в таком случае будет происходить расшаривание переменной, т.е. если она лежала на стеке - переезжает в хип, соответственно финализация будет происходить когда кончаться ссылки на переменную.
![[User Picture]](http://l-userpic.livejournal.com/43376717/9763421) | From: bik_top 2009-05-21 08:48 pm (UTC)
IDisposable и using | (Link)
|
Q>>Наверное, имеется в виду, что если лямбда захватывает переменную, скоуп которой вводится конструкцией using, то всё будет плохо.
Z>Мне кажется, или в таком случае будет происходить расшаривание переменной, т.е. если она лежала на стеке - переезжает в хип, соответственно финализация будет происходить когда кончаться ссылки на переменную.
Финализация не при чём. Может, я неправильно тебя понял, но на всякий случай — маленький ликбез :)
Синтаксис шарповых финализаторов разработчики языка сделали похожим на синтаксис плюсовых деструкторов. И это несомненная ошибка Хейлсберга, потому что финализатор не является аналогом плюсового деструктора. Ближайшим родственником плюсового деструктора является метод Dispose(). Именно он позволяет воплощать идиому RAII.
В шарпе есть конструкция using, которая позволяет гарантировать вызов метода Dispose() при выполнении одного из условий: 1) достижение конца скоупа, 2) выход по return не доходя до конца скоупа, 3) выход из скоупа при возникновении исключения. То есть имеем прямую аналогию с механизмом вызова плюсовых деструкторов. В то время как момент вызова финализатора (если он вообще есть) неопределён (когда там сборщик сработает), вызов Dispose() при использовании using строго детерменирован. Для освобождения неуправляемых ресурсов в C# нужно использовать именно Dispose(), финализатор играет вспомогательную роль, он может как присутствовать, так и отсутствовать.
А теперь приведу пример проблемы, которую я имел в виду. Пусть есть класс, реализующий IDisposable:
class A : IDisposable
{
private bool _valid;
public bool Valid { get { return _valid; } }
public A()
{
Debug.WriteLine("A..ctor()");
_valid = true;
}
public void Dispose()
{
Debug.WriteLine("A.Dispose()");
_valid = false;
}
}
(Здесь вместо «_valid = true;» может стоять захвать какого-нибудь ресурса, а вместо «_valid = false;» — освобождение). Тогда нижеследующий код напечатает в начале скоупа «A..ctor()», а по выходу из скоупа «A.Dispose()»:
using (var a = new A())
{
...
}
Проблема в том, что при копировании ссылки расшаривания владения произвольным ресурсом само по себе не происходит. Поэтому следующий код напечатает «Is valid: False»:
static class EntryPoint
{
static Func<bool> Foo()
{
using (var a = new A())
{
return () => a.Valid;
}
}
static void Main()
{
var foo = Foo();
Console.WriteLine("Is valid: {0}", foo());
}
}
![[User Picture]](http://l-userpic.livejournal.com/94983350/7327447) | From: zabivator 2009-05-21 09:20 pm (UTC)
Re: IDisposable и using | (Link)
|
0) То, что ты написал, я знаю - спасибо ivansorokinГде проблема-то? Вижу два варианта: 1) Dispose вызовется при разрушении foo() 2) Объект скопируется, будет два. У одного Dispose вызовётся при выходе из Foo, у другого при сносе foo() (a-la gc) Какой вариант верен?
![[User Picture]](http://l-userpic.livejournal.com/43376717/9763421) | From: bik_top 2009-05-21 09:49 pm (UTC)
Re: IDisposable и using | (Link)
|
>Где проблема-то?
Проблема подытожена фразой «Проблема в том, что...» из предыдущего коммента.
>Dispose вызовется при разрушении foo()
Что такое «разрушение foo()»? При разрушении foo ничего не вызовется.
>Объект скопируется, будет два.
В данном примере никакие объекты не копируются, только ссылки.
>У одного Dispose вызовётся при выходе из Foo
Не при выходе из Foo(), а при выходе из using-скоупа. Если его там не будет, никакого Dispose() не вызовется.
>у другого при сносе foo() (a-la gc)
Ты о чём? 0_о Здесь не определён финализатор! Поэтому сборка этого объекта сводится просто к освобождению памяти. Ничего вызываться не будет.
![[User Picture]](http://l-userpic.livejournal.com/94983350/7327447) | From: zabivator 2009-05-22 05:32 am (UTC)
Re: IDisposable и using | (Link)
|
Не при выходе из Foo(), а при выходе из using-скоупа. Если его там не будет, никакого Dispose() не вызовется. &&
Ты о чём? 0_о Здесь не определён финализатор! Поэтому сборка этого объекта сводится просто к освобождению памяти. Ничего вызываться не будет. ОЙ! Семантика языка похакана =)
![[User Picture]](http://l-userpic.livejournal.com/43376717/9763421) | From: bik_top 2009-05-22 06:11 am (UTC)
Прискорбно, но факт | (Link)
|
>ОЙ! Семантика языка похакана =)
Да, C# местами более переподвывернут, чем C++.
![[User Picture]](http://l-userpic.livejournal.com/94983350/7327447) | From: zabivator 2009-05-22 07:08 am (UTC)
Re: Прискорбно, но факт | (Link)
|
Кто-то писал, что сложность шарпа и жабы уже сравнима с плюсовой. Иногда я думаю, что это правда =)
мне кажется, что если фина... а, тьфу, сорри. my bad. если обьект имеет финализатор, то он уже находится в хипе
C++ стиль выдержан :) Интересно, этим хоть как-то пользоваться можно будет? Кстати, они же еще GC прикрутили, так что в принципе могли бы сделать нормальные closures. Главный вопрос - нахрена такие фичи в низкоуровневом языке.
Ну как же "нафига"? Алгоритмы STL есть? Есть! А пользоваться ими можно? Нельзя, т.к. надо писать стопицот букв. Вот для этого и сделали. Типичный юс-кейс - такой вот заинкапсулированный цикл: int y = 0;
std::for_each(container, [&](std::string &x)
{
y += x.size();
});А инкапсуляция циклов нужна на каждом шагу, именно копипаст стандартных циклов - основная часть неустранимого копипаста в С++.
Журнальчик прикольный у вас, можно было бы уже и на собственный домен перебираться | |