using + Object Initializer = грабли
От: _FRED_ Черногория
Дата: 27.11.09 11:38
Оценка: 314 (23)
В следующем коде выделенный ресурс (экземпляр MyClass) не будет освобождён
using System;

class Program
{
  static void Main() {
    try {
      using(var x = new MyClass { Value = 1, }) {
        // some work
      }//using
    } catch {
      Console.WriteLine("Catch");
    }//try
  }
}

class MyClass : IDisposable
{
  public MyClass() {
    Console.WriteLine("Ctor");
  }

  public object Value {
    get { return null; }

    set {
      Console.WriteLine("Set property value");
      throw new Exception();
    }
  }

  public void Dispose() {
    Console.WriteLine("Disposed");
  }
}

Будьте бдительны! Жаль, Object Initializer удобная и удачная конструкция, но не везде применима.
Help will always be given at Hogwarts to those who ask for it.
Re: using + Object Initializer = грабли
От: Пельмешко Россия blog
Дата: 27.11.09 12:07
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Будьте бдительны! Жаль, Object Initializer удобная и удачная конструкция, но не везде применима.


А я только недавно узнал про скрытую локальную переменную при инициализации Object Initializer'ом, подумал — вот как хорошо, если при инициализации исключение будет, то инициализируемая локальная останется null, как если бы в конструкторе исключение было, а оно вон какие эффекты даёт...
Re[2]: using + Object Initializer = грабли
От: _FRED_ Черногория
Дата: 27.11.09 12:18
Оценка:
Здравствуйте, Пельмешко, Вы писали:

_FR>>Будьте бдительны! Жаль, Object Initializer удобная и удачная конструкция, но не везде применима.


П>А я только недавно узнал про скрытую локальную переменную при инициализации Object Initializer'ом, подумал — вот как хорошо, если при инициализации исключение будет, то инициализируемая локальная останется null, как если бы в конструкторе исключение было, а оно вон какие эффекты даёт...


Между прочим, в этих граблях виноват… ты Я их увидел, прочитав этот пост
Автор: Пельмешко
Дата: 26.11.09
Help will always be given at Hogwarts to those who ask for it.
Re: using + Object Initializer = грабли
От: alexsoff Россия  
Дата: 27.11.09 12:24
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>В следующем коде выделенный ресурс (экземпляр MyClass) не будет освобождён

Юзинг не будет вызван и в следующем коде:

class Program
{
    static void Main()
    {
        try
        {
            using (var x = new MyClass())
            {
                
            }//using
        }
        catch(Exception ex)
        {
            Console.WriteLine("Catch");
        }//try

        Console.ReadKey();
    }
}

class MyClass : IDisposable
{
    public MyClass()
    {
        Console.WriteLine("Ctor");
        throw new Exception();    }

    public object Value
    {
        get { return null; }

        set
        {
            Console.WriteLine("Set property value");
            throw new Exception();
        }
    }

    /// <summary>
    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
    /// </summary>
    /// <filterpriority>2</filterpriority>
    public void Dispose()
    {
        Console.WriteLine("Disposed");
    }

}

А так как инициалайзеры призваны упростить создание конструкторов со множественными параметрами, этого следует ожидать.
Re[2]: using + Object Initializer = грабли
От: _FRED_ Черногория
Дата: 27.11.09 12:30
Оценка: 7 (2)
Здравствуйте, alexsoff, Вы писали:

_FR>>В следующем коде выделенный ресурс (экземпляр MyClass) не будет освобождён

A>Юзинг не будет вызван и в следующем коде:

Должен быть вызван Dispose()

A>class MyClass : IDisposable
A>{
A>    public MyClass()
A>    {
A>        Console.WriteLine("Ctor");
A>        throw new Exception();    }

A>А так как инициалайзеры призваны упростить создание конструкторов со множественными параметрами, этого следует ожидать.

Общепринятым соглашением (о правильно написанных классах) является то, что если метод выделения ресурса (в данном случае конструктор) завершился неудачей, то вызвавшему метод освобождать ничего не нужно. В данном случае (исключение в конструкторе) ошибку (не освободил ресурс) допустил автор MyClass. Это он должен обеспечить то, что или конструктор отработает успешно, или (в случае неуспеха) утечки ресурса не будет.

В случае же со свойством ресурс уже выделен корректно.
Help will always be given at Hogwarts to those who ask for it.
Re[3]: using + Object Initializer = грабли
От: Пельмешко Россия blog
Дата: 27.11.09 12:35
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Здравствуйте, Пельмешко, Вы писали:


_FR>>>Будьте бдительны! Жаль, Object Initializer удобная и удачная конструкция, но не везде применима.


П>>А я только недавно узнал про скрытую локальную переменную при инициализации Object Initializer'ом, подумал — вот как хорошо, если при инициализации исключение будет, то инициализируемая локальная останется null, как если бы в конструкторе исключение было, а оно вон какие эффекты даёт...


_FR>Между прочим, в этих граблях виноват… ты Я их увидел, прочитав этот пост
Автор: Пельмешко
Дата: 26.11.09


Ыыыы
В F# сюрприз аналогичный:

type MyClass =
    
    new() = { } then printfn "Ctor"
    
    member x.Value
      with get() = null :> obj
      and set(_) = printfn "Set property value"
                   failwith "uups!"
    
    interface System.IDisposable with
      member x.Dispose() = printfn "Disposed"

let main() =
    try  use x = new MyClass(Value = box 1)
         () (* some work *)
    with _ -> printfn "Catch"

> main();;
Ctor
Set property value
Catch

Интересно, а как будет выглядеть правильно сгенерированный код для using в данной ситуации?
Re[3]: using + Object Initializer = грабли
От: alexsoff Россия  
Дата: 27.11.09 12:48
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Должен быть вызван Dispose()

Да, я это имел ввиду


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

Но если прочитать об инициалайзерах:
здесь


Object initializers let you assign values to any accessible fields or properties of an object at creation time without having to explicitly invoke a constructor.


То такое поведение вполне логично. Поля инициализируются в момент создания ресурса. Тем самым, если возникло исключение, то
_FR>то вызвавшему метод освобождать ничего не нужно
Re[4]: using + Object Initializer = грабли
От: nikov США http://www.linkedin.com/in/nikov
Дата: 27.11.09 12:59
Оценка:
Здравствуйте, alexsoff, Вы писали:

A>То такое поведение вполне логично. Поля инициализируются в момент создания ресурса. Тем самым, если возникло исключение, то

_FR>>то вызвавшему метод освобождать ничего не нужно

А кто должен освобождать? Сеттер свойства? А откуда он узнает, что он вызван из object initializer? Я думаю, что дизайн языка здесь не совсем продуман.
Re[4]: using + Object Initializer = грабли
От: _FRED_ Черногория
Дата: 27.11.09 13:00
Оценка: +2
Здравствуйте, alexsoff, Вы писали:

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

A>Но если прочитать об инициалайзерах:
A>здесь

A>Object initializers let you assign values to any accessible fields or properties of an object at creation time without having to explicitly invoke a constructor.

A>То такое поведение вполне логично.

Что здесь значит "такое"?

A>Поля инициализируются в момент создания ресурса.


В том-то и дело, что нет! Object initializers создают видимость того, что "Поля инициализируются в момент создания ресурса", но по факту это различные (не атомарные) операции.

A>Тем самым, если возникло исключение, то

_FR>>то вызвавшему метод освобождать ничего не нужно

Вот ведь какое дело: во втором шарпе проблем не возникало:
using(Resource x = new Resource()) {
  x.Value = xxx;
}//using

а с появлением нового синтаксиса, его (синтаксиса) использование может привести к проблемам и тут автор класса бессилен.

То есть правильно, по идее должен генериться такой код, что "вызвавшему метод освобождать ничего не нужно", но этого не происходит. Тот программист, что не понимает, как устроен Object initializer, может легко пропустить такую тонкость.
Help will always be given at Hogwarts to those who ask for it.
Re[5]: using + Object Initializer = грабли
От: _FRED_ Черногория
Дата: 27.11.09 13:02
Оценка:
Здравствуйте, nikov, Вы писали:

A>>То такое поведение вполне логично. Поля инициализируются в момент создания ресурса. Тем самым, если возникло исключение, то

_FR>>>то вызвавшему метод освобождать ничего не нужно

N>А кто должен освобождать? Сеттер свойства? А откуда он узнает, что он вызван из object initializer?


Конечно нет.

N>Я думаю, что дизайн языка здесь не совсем продуман.


Именно, но, ИМХО, не столько языка (того, что мы пишем), сколько реализации Object Initializer (того, что происходит в компиляторе).
Help will always be given at Hogwarts to those who ask for it.
Re[5]: using + Object Initializer = грабли
От: alexsoff Россия  
Дата: 27.11.09 13:03
Оценка:
Здравствуйте, nikov, Вы писали:

N>А кто должен освобождать? Сеттер свойства? А откуда он узнает, что он вызван из object initializer? Я думаю, что дизайн языка здесь не совсем продуман.

Так, стоп. Всмысле кто должен освобождать? сказано же в контексте инишиалайзера, думается что ресурсы еще не выделялись, это все равно, что вы создали бы параметры в конструкторе и внутри бы инициализировали свойста.
Откуда свойства знают, что они инициализируются в конструкторе?
Re[3]: using + Object Initializer = грабли
От: cadet354 Россия
Дата: 27.11.09 13:09
Оценка:
Здравствуйте, _FRED_, Вы писали:

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


_FR>>>В следующем коде выделенный ресурс (экземпляр MyClass) не будет освобождён

A>>Юзинг не будет вызван и в следующем коде:

_FR>Должен быть вызван Dispose()

точно вызовется? мои тесты показали вывод

Ctor
Catch

да и не должен вызываться это же эквивалент такого:
var x = new MyClass();
try{
    x.Value=1;
}
finally{
    x.Dispose();
}
... << RSDN@Home 1.2.0 alpha 4 rev. 1270>>
Re[4]: using + Object Initializer = грабли
От: _FRED_ Черногория
Дата: 27.11.09 13:15
Оценка:
Здравствуйте, cadet354, Вы писали:

_FR>>>>В следующем коде выделенный ресурс (экземпляр MyClass) не будет освобождён

A>>>Юзинг не будет вызван и в следующем коде:
_FR>>Должен быть вызван Dispose()
C>точно вызовется? мои тесты показали вывод

Тесты чего? Покажи, что тестишь :о)) Оригинальный код или пример alexsoff?
Help will always be given at Hogwarts to those who ask for it.
Re[4]: using + Object Initializer = грабли
От: Воронков Василий Россия  
Дата: 27.11.09 13:16
Оценка:
Здравствуйте, cadet354, Вы писали:

C>да и не должен вызываться это же эквивалент такого:


Диспоз должен быть вызван ручками в конструкторе.
Re[5]: using + Object Initializer = грабли
От: alexsoff Россия  
Дата: 27.11.09 13:16
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>В том-то и дело, что нет! Object initializers создают видимость того, но по факту это различные (не атомарные) операции.

Ну здрасти, я же Вам и ссылку в msdn и процитировал. Что msdn вводит в заблуждение?


Object initializers let you assign values to any accessible fields or properties of an object at creation time without having to explicitly invoke a constructor



_FR>а с появлением нового синтаксиса

Нужно хорошенько ознакомиться с ньюансами

PS: Извините за сорказм
Re[5]: using + Object Initializer = грабли
От: cadet354 Россия
Дата: 27.11.09 13:41
Оценка:
Здравствуйте, _FRED_, Вы писали:



_FR>Тесты чего? Покажи, что тестишь :о)) Оригинальный код или пример alexsoff?

#region

using System;

#endregion

namespace TestUsing
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            try
            {
                using (var x = new MyClass())
                {
                    x.Value = 1;
                    // some work
                } //using
            }
            catch
            {
                Console.WriteLine("Catch");
            } //try
            finally
            {
                Console.ReadLine();
            }
        }
    }


    internal class MyClass : IDisposable
    {
        public MyClass()
        {
            Console.WriteLine("Ctor");
            throw new Exception();
        }

        public object Value
        {
            get { return null; }

            set { Console.WriteLine("Set property value"); }
        }

        #region IDisposable Members

        public void Dispose()
        {
            Console.WriteLine("Disposed");
        }

        #endregion
    }
}
... << RSDN@Home 1.2.0 alpha 4 rev. 1270>>
Re[5]: using + Object Initializer = грабли
От: cadet354 Россия
Дата: 27.11.09 13:47
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>а с появлением нового синтаксиса, его (синтаксиса) использование может привести к проблемам и тут автор класса бессилен.

это не эквивалентный код, вот эквивалент
using(Resource x = CreateNewResource()) {

}//using
Resource CreateNewResource(){
    Resource t=new Resource();
    t.Value=xxx;
    return t;
}

и совершенно логично что диспоза не будет
... << RSDN@Home 1.2.0 alpha 4 rev. 1270>>
Re[6]: using + Object Initializer = грабли
От: _FRED_ Черногория
Дата: 27.11.09 13:48
Оценка:
Здравствуйте, cadet354, Вы писали:

_FR>>Тесты чего? Покажи, что тестишь :о)) Оригинальный код или пример alexsoff?

C>                using (var x = new MyClass())
C>                {
C>                    x.Value = 1;
C>                    // some work
C>                } //using


И что в этом примере смущает? при возникновении исключения в точке выделения ресурса (в конструкторе) вызывать Dispose и не нужно, потому что считается, что ресурс не выделился я же ровно об этом в ответ и писал. Если что-то не ясно оказалось — спроси пожалуйста конкретнее.
Help will always be given at Hogwarts to those who ask for it.
Re[6]: using + Object Initializer = грабли
От: _FRED_ Черногория
Дата: 27.11.09 13:51
Оценка: +1
Здравствуйте, cadet354, Вы писали:

_FR>>а с появлением нового синтаксиса, его (синтаксиса) использование может привести к проблемам и тут автор класса бессилен.

C>это не эквивалентный код, вот эквивалент
C>using(Resource x = CreateNewResource()) {

C>}//using
C>Resource CreateNewResource(){
C>    Resource t=new Resource();
C>    t.Value=xxx;
C>    return t;
C>}

C>и совершенно логично что диспоза не будет

Здесь на лицо криво написанный метод выделения ресурса. Люди, заботящиеся о своей карме, делают так:
C>Resource CreateNewResource() {
C>  Resource t = new Resource();
    try {
C>    t.Value = xxx;
    } catch {
      t.Dispose();
      throw;
    }//try
C>  return t;
C>}

Теперь совершенно безопасно делать так:
C>using(Resource x = CreateNewResource()) {

C>}//using
Help will always be given at Hogwarts to those who ask for it.
Re[7]: using + Object Initializer = грабли
От: alexsoff Россия  
Дата: 27.11.09 14:01
Оценка:
Здравствуйте, _FRED_, Вы писали:
C>>Resource CreateNewResource() {
C>> Resource t = new Resource();
_FR> try {
C>> t.Value = xxx;
_FR> } catch {
_FR> t.Dispose();
_FR> throw;
_FR> }//try
Почему так правильно? А не так:


MyClass cl  = new MyClass();
        try
        {
            cl.Value = "111";
            
        }finally {
            cl.Dispose();
        }
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.