Стоит ли переносить инициализацию в отдельный метод?
От: Аноним  
Дата: 02.07.13 03:23
Оценка:
Есть ли у этого подхода преимущества перед обычной инициализацией через конструктор в C#?
Re: Стоит ли переносить инициализацию в отдельный метод?
От: Sinix  
Дата: 02.07.13 04:55
Оценка: 1 (1) -1
Здравствуйте, Аноним, Вы писали:

А>Есть ли у этого подхода преимущества перед обычной инициализацией через конструктор в C#?


It depends. Например, если инициализация включает в себя логику, специфичную для отдельных сценариев, вместо двух конструкторов удобней использовать static-методы вида CreateForA(), CreateForB().

Если речь идёт про выносе всего кода из конструктора в отдельный метод — практически не встречал. В большинстве случаев удобней разбить логику на отдельные методы и превратить инициализацию в вызовы этих методов из конструктора.
Re[2]: Стоит ли переносить инициализацию в отдельный метод?
От: Аноним  
Дата: 02.07.13 05:31
Оценка:
Здравствуйте, Sinix, Вы писали:

S>It depends. Например, если инициализация включает в себя логику, специфичную для отдельных сценариев, вместо двух конструкторов удобней использовать static-методы вида CreateForA(), CreateForB().


S>Если речь идёт про выносе всего кода из конструктора в отдельный метод — практически не встречал. В большинстве случаев удобней разбить логику на отдельные методы и превратить инициализацию в вызовы этих методов из конструктора.


Всё так. Спасибо.
Re: Стоит ли переносить инициализацию в отдельный метод?
От: Nikolay_Ch Россия  
Дата: 02.07.13 05:38
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Есть ли у этого подхода преимущества перед обычной инициализацией через конструктор в C#?

Ну... к примеру, если активно используются интерфейсы, то единственный нормальный способ проинициализировать экземпляр — это метод.
Re[2]: Стоит ли переносить инициализацию в отдельный метод?
От: Аноним  
Дата: 02.07.13 06:12
Оценка: +2
Здравствуйте, Nikolay_Ch, Вы писали:
N_C>Ну... к примеру, если активно используются интерфейсы, то единственный нормальный способ проинициализировать экземпляр — это метод.

А можете раскрыть мысль? Совершенно не улавливается связь между инициализацией и интерфейсами.

ИМХО существование объекта в невалидном состоянии между вызовами публичных методов — плохая практика. Соответственно и публичные методы инициализации, которые позволяют существовать объекту в невалидном состоянии в течении времени между созданием объекта и вызовом метода инициализации, есть нехорошо. Единственное оправдание всяких Form.OnCreate Control.Initialize это низкая сложность входа, отсутствие необходимости объяснять что такое Dependency Injection и возможность скрыть сложность фабрики форм.
Re[3]: Стоит ли переносить инициализацию в отдельный метод?
От: Nikolay_Ch Россия  
Дата: 02.07.13 06:31
Оценка:
Здравствуйте, Аноним, Вы писали:

А>А можете раскрыть мысль? Совершенно не улавливается связь между инициализацией и интерфейсами.

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

А>ИМХО существование объекта в невалидном состоянии между вызовами публичных методов — плохая практика.

ИМХО практика нормальная. К примеру как при работе с БД — у объекта Connection есть конструктор, а есть метод Open. Таких ситуаций множество можно описать.
Re[4]: Стоит ли переносить инициализацию в отдельный метод?
От: Аноним  
Дата: 02.07.13 07:02
Оценка:
Здравствуйте, Nikolay_Ch, Вы писали:

N_C>Здравствуйте, Аноним, Вы писали:


А>>А можете раскрыть мысль? Совершенно не улавливается связь между инициализацией и интерфейсами.

N_C>Если у Вас есть интерфейс, который реализуется совершенно разными объектами (не наследуемых друг от друга). Есть объекты, которые получают на вход экземпляр объекта и должны его инициализировать. Вам как-то надо придумать единый метод инициализации этих объектов, т.к. конструктор здесь не подойдет.
Как конструктор может не подойти для инициализации объекта?
Я правильно понял вашу мысль: кто-то(кто?) создал объекты и теперь просит их проинициализировать уже у вашего клиента. Возникает естественный вопрос: почему этот кто-то не создал их сразу нормальными? И кто будет инициализировать объекты, если они понадобятся в другом месте?

А>>ИМХО существование объекта в невалидном состоянии между вызовами публичных методов — плохая практика.

N_C>ИМХО практика нормальная. К примеру как при работе с БД — у объекта Connection есть конструктор, а есть метод Open. Таких ситуаций множество можно описать.
Вы тут подменяете инициализацию и открытие соединения. Объект соединение в закрытом состоянии это вполне валидный объект, с явно определенным поведением. И открытие соединения это часть интерфейса.

Похоже термин "инициализация объекта" мы наделяем разными понятиями. Я под этим термином понимаю некое наполнение внутреннего состояния объекта. И это состояние должно быть валидным с точки зрения публичного интерфейса.
Re: UninitializedException
От: igor-booch Россия  
Дата: 02.07.13 07:33
Оценка: +2
А>Есть ли у этого подхода преимущества перед обычной инициализацией через конструктор в C#?

Есть недостаток, нужно во всех методах проверять проинициализирован экземпляр или нет, и если нет выкидывать эксепшен.
А при инициализации через конструктор, у Вас либо есть проинициализированный экземпляр, либо его нет.
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re[2]: Стоит ли переносить инициализацию в отдельный метод?
От: Tom Россия http://www.RSDN.ru
Дата: 02.07.13 09:25
Оценка:
А>>Есть ли у этого подхода преимущества перед обычной инициализацией через конструктор в C#?
S>It depends. Например, если инициализация включает в себя логику, специфичную для отдельных сценариев, вместо двух конструкторов удобней использовать static-методы вида CreateForA(), CreateForB().
Это до того момента когда вам тесты понадобится писать. В целом для нормально тестируемого кода надо забыть что такое статический метод, или статическая переменная
Народная мудрось
всем все никому ничего(с).
Re[2]: UninitializedException
От: drol  
Дата: 02.07.13 09:30
Оценка:
Здравствуйте, igor-booch, Вы писали:

IB>А при инициализации через конструктор, у Вас либо есть проинициализированный экземпляр, либо его нет.


Это верно только для однопоточной ситуации. В многопоточном случае возможны такие "чудеса", как выполнение конструктора параллельно с финализатором.
Re[3]: UninitializedException
От: HowardLovekraft  
Дата: 02.07.13 10:21
Оценка:
Здравствуйте, drol, Вы писали:

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

Пруф?
Финализаторы и так в отдельном потоке вызываются. Что вы подразумеваете под словом "многопоточный" в данном случае?
Re[3]: Стоит ли переносить инициализацию в отдельный метод?
От: Аноним  
Дата: 02.07.13 10:22
Оценка:
Здравствуйте, Аноним, Вы писали:

А>А можете раскрыть мысль? Совершенно не улавливается связь между инициализацией и интерфейсами.

Для инициализации используется конструктор. Конструктор не может быть определен в интерфейсе.
Re[3]: Стоит ли переносить инициализацию в отдельный метод?
От: Sinix  
Дата: 02.07.13 10:25
Оценка: +1 :)
Здравствуйте, Tom, Вы писали:

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


Или не нагибать код под тесты. Можно просто использовать Fakes/Mole, можно сочетать тесты с ассертами, можно вообще отказаться от юнит-тестов, обвешать код контрактами и ловить оставшуюся мелочь сразу на интеграционных/функциональных тестах. Я видел все три варианта, не сказал бы что любой из них был сильно лучше/хуже прочих.
Re: Стоит ли переносить инициализацию в отдельный метод?
От: HowardLovekraft  
Дата: 02.07.13 10:25
Оценка: +1
Здравствуйте, Аноним, Вы писали:

А>Есть ли у этого подхода преимущества перед обычной инициализацией через конструктор в C#?

Что имеется ввиду? Это:
class MyClass
{
    private MyClass()
    {
    }    

    public MyClass Create();
}

это:
class MyClass()
{
    private void Initialize()
    {
    }

    public MyClass()
    {
        Initialize();
    }
}

или еще какой-то вариант?
Re[5]: Стоит ли переносить инициализацию в отдельный метод?
От: Аноним  
Дата: 02.07.13 10:28
Оценка:
Здравствуйте, Аноним, Вы писали:

N_C>>ИМХО практика нормальная. К примеру как при работе с БД — у объекта Connection есть конструктор, а есть метод Open. Таких ситуаций множество можно описать.

А>Вы тут подменяете инициализацию и открытие соединения. Объект соединение в закрытом состоянии это вполне валидный объект, с явно определенным поведением. И открытие соединения это часть интерфейса.
Почему?
Допустим, имеется объект view сразу после вызова new View(). И имеется объект view после вызова view.Initialize(some data), заполненный некоторыми данными.
Что невалидного в состоянии объекта view без данных?
Re[2]: Стоит ли переносить инициализацию в отдельный метод?
От: Аноним  
Дата: 02.07.13 10:35
Оценка:
Первое.
Точнее, так: в чем преимущество:
public class MyClass
{
    public MyClass()
    {
    }    

    public MyClass Initialize(...);
}

перед:
public class MyClass
{
    public MyClass(...)
    {
    }
}
Re[3]: Стоит ли переносить инициализацию в отдельный метод?
От: Sinatr Германия  
Дата: 02.07.13 11:16
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Точнее, так: в чем преимущество:

А> public MyClass Initialize(...);
А>перед:
А> public MyClass(...)

Отложенная инициализация. Удобно, когда нужно инстанцинировать обьект, а инициализировать попозже (не в момент инстанцинации). Частный случай применения — если инстанцинация относительно "дорого" обходится, т.к. в этом случае есть возможность ее избежать, вызвав Deinit+Init.
---
ПроГLамеры объединяйтесь..
Re[3]: Стоит ли переносить инициализацию в отдельный метод?
От: HowardLovekraft  
Дата: 02.07.13 11:34
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Первое.

Обнаружил опечатку в своем посте. Должно быть так:
class MyClass
{
    private MyClass()
    {
    }    

    public static MyClass Create() {}
}

Т.е., фабричный метод.

А>Точнее, так: в чем преимущество:

А>перед:
Объект создан, но не инициализирован. Т.е. пользоваться им нельзя. Вопросы: 1) зачем его создавать? 2) тут есть преимущество?
Соседний пост про отложенную инициализацию не катит. Отложенная инициализация должна быть скрыта от внешнего кода.
Re[4]: Стоит ли переносить инициализацию в отдельный метод?
От: artelk  
Дата: 02.07.13 12:38
Оценка:
Здравствуйте, HowardLovekraft, Вы писали:

Однажды такое понадобилось, чтобы запретить клиентам наследовать от моего абстрактного класса Either — т.е. сделать закрытую иерархию (эмуляция алгебраических типов):
  Скрытый текст
public abstract class Either<T1, T2>
{
    private Either()
    { }

    public static Either<T1, T2> New(T1 value)
    {
        return Value1(value);
    }

    public static Either<T1, T2> New(T2 value)
    {
        return Value2(value);
    }

    public static Either<T1, T2> Value1(T1 value)
    {
        return new Either1(value);
    }

    public static Either<T1, T2> Value2(T2 value)
    {
        return new Either2(value);
    }

    public abstract T Exec<T>(Func<T1, T> f1, Func<T2, T> f2);

    public abstract void Exec(Action<T1> f1, Action<T2> f2);

    private sealed class Either1 : Either<T1, T2>
    {
        private readonly T1 _value;

        public Either1(T1 value)
        {
            _value = value;
        }

        public override T Exec<T>(Func<T1, T> f1, Func<T2, T> f2)
        {
            return f1(_value);
        }

        public override void Exec(Action<T1> f1, Action<T2> f2)
        {
            f1(_value);
        }
    }

    private sealed class Either2 : Either<T1, T2>
    {
        private readonly T2 _value;

        public Either2(T2 value)
        {
            _value = value;
        }

        public override T Exec<T>(Func<T1, T> f1, Func<T2, T> f2)
        {
            return f2(_value);
        }

        public override void Exec(Action<T1> f1, Action<T2> f2)
        {
            f2(_value);
        }
    }
}
Re[5]: Стоит ли переносить инициализацию в отдельный метод?
От: HowardLovekraft  
Дата: 02.07.13 13:14
Оценка:
Здравствуйте, artelk, Вы писали:

A>skipped

Не, у вас как раз пример с фабричными методами.
А у ТС Initialize вполне себе экземплярный и public:
var myObj = new MyClass();
myObj.Initialize();
Re[4]: UninitializedException
От: drol  
Дата: 02.07.13 13:59
Оценка:
Здравствуйте, HowardLovekraft, Вы писали:

HL>Пруф?


Читайте спецификацию... Ну и Липперта...

HL>Что вы подразумеваете под словом "многопоточный" в данном случае?


Вызов методов объекта в разных потоках. Никаких гарантий синхронизации для вызова конструктора не даётся. В этом плане он ничем не отличается от прочих instance-методов.
Re: Стоит ли переносить инициализацию в отдельный метод?
От: abibok  
Дата: 02.07.13 15:00
Оценка:
А>Есть ли у этого подхода преимущества перед обычной инициализацией через конструктор в C#?

Если код инициализации может бросать исключения, то лучше вынести его в отдельный метод. Особенно если класс реализует IDisposable.
Re[2]: Стоит ли переносить инициализацию в отдельный метод?
От: artelk  
Дата: 02.07.13 15:08
Оценка:
Здравствуйте, abibok, Вы писали:

A>Если код инициализации может бросать исключения, то лучше вынести его в отдельный метод.


Ага,
class A
{
  A(ISomething s)
  {
    if(s == null)
      throw new ArgumentNullException("s");
    this.s = s;
  }
}

Срочно вынести в отдельный метод!
Re[3]: Стоит ли переносить инициализацию в отдельный метод?
От: abibok  
Дата: 02.07.13 16:56
Оценка:
A>Срочно вынести в отдельный метод!

Не надо поясничать. Есть проверка аргументов и примитивная логика инициализации вроде присвоения начальных значений полям. Этому делу самое место в конструкторе.
А есть тяжелый код с выделением ресурсов, так что конструктор выглядит явно переусложненным. Если такой конструктор бросит исключение в using (Foo foo = new Foo()), то Dispose не будет вызван и ресурсы останутся неосвобожденными.
Re[4]: Стоит ли переносить инициализацию в отдельный метод?
От: artelk  
Дата: 02.07.13 17:29
Оценка:
Здравствуйте, abibok, Вы писали:

A>>Срочно вынести в отдельный метод!


A>Не надо поясничать. Есть проверка аргументов и примитивная логика инициализации вроде присвоения начальных значений полям. Этому делу самое место в конструкторе.

A>А есть тяжелый код с выделением ресурсов, так что конструктор выглядит явно переусложненным. Если такой конструктор бросит исключение в using (Foo foo = new Foo()), то Dispose не будет вызван и ресурсы останутся неосвобожденными.

Ок, допустим сделали мы так:
class Foo : IDisposable
{
  public Foo(...)
  {
    //проверка аргументов и примитивная логика инициализации вроде присвоения начальных значений полям.
  }

  public void Initialize()
  {
    //тяжелый код с выделением ресурсов
  }

  public void Dispose()
  {
    if(disposed)
      return;
    //проверяем, какие ресурсы были выделены и освобождаем их
  }
}

using (Foo foo = new Foo())
{
  //...
  foo.Initialize()
  //...
}


Метод Initialize может быть и не вызван вовсе. Или в нем в середине было выкинуто исключение и не все ресурсы были выделены. Поэтому в Dispose нужно вставить проверки вида
if(someresource != null)
  someresource.Dispose();



Но что мешает нам переписать этот кода как-то так:
class Foo : IDisposable
{
  public Foo(...)
  {
    //проверка аргументов и примитивная логика инициализации вроде присвоения начальных значений полям.

    try
    {
      Initialize();
    }
    catch
    {
      Dispose();
      throw;
    }
  }

  private void Initialize()
  {
    //тяжелый код с выделением ресурсов
  }

  public void Dispose()
  {
    if(disposed)
      return;
    //проверяем, какие ресурсы были выделены и освобождаем их
  }
}
?

Это избавило бы клиентский код от необходимости вызывать Initialize().
Более того, в классе Foo не пришлось бы делать дополнительных приседаний, чтобы корректно работать в ситуации со множественными вызовами Initialize() — метод торчит наружу, никто не запрещает его вызвать дважды.
Re[5]: Стоит ли переносить инициализацию в отдельный метод?
От: abibok  
Дата: 02.07.13 17:50
Оценка:
A>Метод Initialize может быть и не вызван вовсе.

Вы подменяете одну проблему другой. Речь шла о дизайне конструктора, а не о контроле за правильностью соблюдения контракта класса.

A>Или в нем в середине было выкинуто исключение и не все ресурсы были выделены. Поэтому в Dispose нужно вставить проверки вида


Конечно, а разве вы делаете иначе? Знаете чем опасны исключения, брошенные в Dispose в частности и в finally вообще?
Re[6]: Стоит ли переносить инициализацию в отдельный метод?
От: artelk  
Дата: 02.07.13 18:12
Оценка:
Здравствуйте, abibok, Вы писали:

A>>Метод Initialize может быть и не вызван вовсе.

A>Вы подменяете одну проблему другой. Речь шла о дизайне конструктора, а не о контроле за правильностью соблюдения контракта класса.
Разве?

Есть ли у этого подхода преимущества перед обычной инициализацией через конструктор в C#?

Где тут про дизайн конструктора? Здесь альтернатива конструктор vs отдельный метод инициализации.
Re[7]: Стоит ли переносить инициализацию в отдельный метод?
От: abibok  
Дата: 02.07.13 18:34
Оценка: :)
A>>Вы подменяете одну проблему другой. Речь шла о дизайне конструктора, а не о контроле за правильностью соблюдения контракта класса.
A>Разве?

Да.

A>

Есть ли у этого подхода преимущества перед обычной инициализацией через конструктор в C#?

A>Где тут про дизайн конструктора? Здесь альтернатива конструктор vs отдельный метод инициализации.

Дизайн = "Должен ли код инициализации находиться внутри конструктора или его надо выносить в отдельный метод. И в каких случаях каждое из решений имеет преимущества".
А про "Забыли вызвать Initialize" — это ваши домыслы. Есть что сказать по существу вопроса?
Re[4]: Стоит ли переносить инициализацию в отдельный метод?
От: Аноним  
Дата: 03.07.13 03:34
Оценка:
Здравствуйте, abibok, Вы писали:

A>А есть тяжелый код с выделением ресурсов, так что конструктор выглядит явно переусложненным. Если такой конструктор бросит исключение в using (Foo foo = new Foo()), то Dispose не будет вызван и ресурсы останутся неосвобожденными.

Спасибо. Именно этот момент и интересовал.

Просьба пояснить.
A>>>Если код инициализации может бросать исключения, то лучше вынести его в отдельный метод. Особенно если класс реализует IDisposable.
Если код инициализации не выделяет ресурсов (но может бросать исключения), то почему лучше тоже вынести его в отдельный метод?
Re[5]: Стоит ли переносить инициализацию в отдельный метод?
От: abibok  
Дата: 03.07.13 05:22
Оценка: 2 (1)
А>Просьба пояснить.
A>>>>Если код инициализации может бросать исключения, то лучше вынести его в отдельный метод. Особенно если класс реализует IDisposable.
А>Если код инициализации не выделяет ресурсов (но может бросать исключения), то почему лучше тоже вынести его в отдельный метод?

Потому что на практике ожидается, что конструктор не бросает исключений, не связанных с проверкой аргументов.
Т.е. сначала создают объект, а потом заворачивают в try-catch вызовы его методов.
Конструктор, бросающий исключения из тяжелой логики внутри — это примерно того же уровня бяка, что property getter, бросающий исключение из тяжелой логики внутри. Никто не запрещает, и в ряде случаев так и надо делать, но по возможности стоит избегать.

А представьте себе, что вы создаете объект некоторого класса не через new, а используя reflection. Исключение из конструктора завернется в какой-нибудь TargetInvocationException и настоящий call stack потеряется. А если хочется перехватить исключение до того, как оно было брошено (чтобы сохранить хороший дамп, например), то вместо простого создания объекта через активатор придется делать вот так:

        public static object CreateInstance(Type type, object[] arguments)
        {
            if (arguments == null || arguments.Length == 0)
            {
                Expression newExp = Expression.New(type);
                Func<object> activator = Expression.Lambda<Func<object>>(Expression.Convert(newExp, typeof(object))).Compile();
                return activator();
            }
            else
            {
                Type[] argTypes = arguments.Select(arg => arg.GetType()).ToArray();
                ConstructorInfo constructor = type.GetConstructor(argTypes);

                if (constructor == null)
                {
                    string msg = String.Format(
                        "Constructor on type {0} that takes in parameters of type [{1}] not found.",
                        type,
                        String.Join<Type>(",", argTypes));
                    throw new MissingMethodException(msg);
                }

                ParameterExpression param = Expression.Parameter(typeof(object[]), "args");
                List<Expression> argsExp = new List<Expression>();

                for (int i = 0; i < arguments.Length; i++)
                {
                    Expression paramAccessorExp = Expression.ArrayIndex(param, Expression.Constant(i));
                    Expression paramCastExp = Expression.Convert(paramAccessorExp, argTypes[i]);
                    argsExp.Add(paramCastExp);
                }

                Expression newExp = Expression.New(constructor, argsExp);
                Func<object[], object> activator = Expression.Lambda<Func<object[], object>>(newExp, param).Compile();

                return activator(arguments);
            }
        }
Re[6]: Стоит ли переносить инициализацию в отдельный метод?
От: Sinix  
Дата: 03.07.13 06:09
Оценка:
Здравствуйте, abibok, Вы писали:

A>А представьте себе, что вы создаете объект некоторого класса не через new, а используя reflection. Исключение из конструктора завернется в какой-нибудь TargetInvocationException и настоящий call stack потеряется. А если хочется перехватить исключение до того, как оно было брошено (чтобы сохранить хороший дамп, например), то вместо простого создания объекта через активатор придется делать вот так:


[километр кода скипнут]

Или просто посмотреть содержимое ex.InnerException
Re[5]: UninitializedException
От: HowardLovekraft  
Дата: 03.07.13 07:55
Оценка: -1
Здравствуйте, drol, Вы писали:

D>Читайте спецификацию...

Спецификацию чего (ссылку на документ + номер пункта/параграфа)?
D>Ну и Липперта...
Ссылку на пост, в котором это описано, пожалуйста.

Утверждение, что CLR может выполнить вызов финализатора для объекта, у которого в данный момент времени выполняется конструктор, выглядит несколько... хм... нелогичным.
Чтоб CLR позвала финализатор, должна произойти сборка мусора, после которой ссылка на объект будет помещена в F-reachable queue. Соответственно, объект должен быть недостижим. Если в какой-то момент времени выполняется конструктор объекта, это значит, что он очень даже достижим.

D>Вызов методов объекта в разных потоках. Никаких гарантий синхронизации для вызова конструктора не даётся

Приведите пример кода, в котором (хотя бы потенциально) может возникнуть одновременный вызов конструктора у одного и того же объекта.
Re[6]: Воспроизвести не вышло
От: igor-booch Россия  
Дата: 03.07.13 09:16
Оценка: :)
Как не старался воспроизвести то, о чем говорит drol не вышло


Foo foo = new Foo();
Thread instantiationThread = new Thread(() =>
{
    do
    {
        foo = new Foo();
    } while (true);
});

Thread checkInitializedThread = new Thread(() =>
{
    do
    {
        if (!foo._initialized1 | !foo._initialized2)
        {
            Console.WriteLine("Bingo!!!");
        }
    } while (true);
});

instantiationThread.Start();
checkInitializedThread.Start();



class Foo
{
    public bool _initialized1;
    public bool _initialized2;

    public Foo()
    {
        //Thread.Sleep(10);
        _initialized1 = true;
        Thread.Sleep(10);
        _initialized2 = true;
    }
}



Вот еще аналогичное мнение
http://rsdn.ru/forum/dotnet/5200476.1
Автор: TK
Дата: 14.06.13

может это навеяло http://msdn.microsoft.com/en-us/magazine/jj863136.aspx
?
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re[7]: Воспроизвести не вышло
От: HowardLovekraft  
Дата: 03.07.13 10:28
Оценка:
Здравствуйте, igor-booch, Вы писали:

IB>skipped

"Неинициализированный" (то, о чем пишет TK) и "недостижимый" (то, что делает объект целью GC) — это разные вещи. Ждем пруфлинков.
Re[8]: Воспроизвести не вышло
От: igor-booch Россия  
Дата: 03.07.13 10:39
Оценка:
HL>"Неинициализированный" (то, о чем пишет TK) и "недостижимый" (то, что делает объект целью GC) — это разные вещи. Ждем пруфлинков.

Я имел ввиду что возможность доступа к не инициализированному объекту это ступенька к недостижимости до не инициализированного объекта.
Если доступ к не инициализированному в конструкторе объекту невозможен, то и невозможна потеря ссылки на этот объект до конца его инициализации.
Так как сначала мы получаем доступ (присваиваем переменной ссылку на экземпляр), и только потом мы можем потерять доступ, присвоив переменной ссылку на другой экземпляр (или null).
Отсутствие ссылок на экземпляр это его недостижимость.
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re[8]: Пускай лучше drol приведет пример кода
От: igor-booch Россия  
Дата: 03.07.13 10:47
Оценка: -1
HL>"Неинициализированный" (то, о чем пишет TK) и "недостижимый" (то, что делает объект целью GC) — это разные вещи. Ждем пруфлинков.
Написать в интернетах могут все-что угодно. Пример кода это железный proof.
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re[6]: UninitializedException
От: Sinix  
Дата: 03.07.13 11:03
Оценка: 4 (1) +1
Здравствуйте, HowardLovekraft, Вы писали:

HL>Если в какой-то момент времени выполняется конструктор объекта, это значит, что он очень даже достижим.


Необязательно.
http://ericlippert.com/2013/06/10/construction-destruction/
Re[6]: Конструктор не бросает исключений, не связанных с проверкой аргументов
От: igor-booch Россия  
Дата: 03.07.13 11:22
Оценка:
A>Потому что на практике ожидается, что конструктор не бросает исключений, не связанных с проверкой аргументов.

Почему? Проблема с Dispose решена здесь
Автор: artelk
Дата: 02.07.13
.
Какие еще могут быть проблемы?
А Initialize(...) тоже не может выкидывать исключения не связанные с проверкой аргументов?
Проверка аргументов понятие растяжимое.
Она может быть любой сложности.
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re[6]: Стоит ли переносить инициализацию в отдельный метод?
От: artelk  
Дата: 03.07.13 11:54
Оценка:
Здравствуйте, abibok, Вы писали:

A>А представьте себе, что вы создаете объект некоторого класса не через new, а используя reflection. Исключение из конструктора завернется в какой-нибудь TargetInvocationException и настоящий call stack потеряется.

Есть несколько решений этой проблемы:
http://stackoverflow.com/questions/57383/in-c-how-can-i-rethrow-innerexception-without-losing-stack-trace
Re[7]: Воспроизвести не вышло
От: bl-blx Россия http://yegodm.blogspot.com
Дата: 03.07.13 12:13
Оценка:
Здравствуйте, igor-booch, Вы писали:

IB>Как не старался воспроизвести то, о чем говорит drol не вышло

IB>
IB>Foo foo = new Foo();
IB>Thread instantiationThread = new Thread(() =>
IB>{
IB>    do
IB>    {
IB>        foo = new Foo();
IB>    } while (true);
IB>});

IB>Thread checkInitializedThread = new Thread(() =>
IB>{
IB>    do
IB>    {
IB>        if (!foo._initialized1 | !foo._initialized2)
IB>        {
IB>            Console.WriteLine("Bingo!!!");
IB>        }
IB>    } while (true);
IB>});

IB>instantiationThread.Start();
IB>checkInitializedThread.Start();
IB>


Вы уверены, что checkInitializedThread увидит значение foo, отличное от первого new Foo()?
El pueblo unido jamás será vencido.
Re[8]: Хороший вопрос
От: igor-booch Россия  
Дата: 03.07.13 12:45
Оценка:
BB>Вы уверены, что checkInitializedThread увидит значение foo, отличное от первого new Foo()?

Хороший вопрос, Вы наверное имеете ввиду Access to modified closure.
Проверил, нет, же, видит прекрасно новые Foo.
И Resharper не ругается на Access to modified closure.

Сделал предположение, что Access to modified closure возникает только если переменная foo будет меняться в том же контексте, в котором создавалась лямбда её использующая:


            Foo foo = new Foo();
            int i = 0;

            Thread checkInitializedThread = new Thread(() =>
            {
                do
                {
                    if (!foo._initialized1 | !foo._initialized2)
                    {
                        Console.WriteLine("Bingo!!!");
                    }
                    Console.WriteLine(foo._i);
                } while (true);
            });

            checkInitializedThread.Start();

            do
            {
                foo = new Foo();
                foo._i = i;
                i++;
            } while (true);




class Foo
{
    public bool _initialized1;
    public bool _initialized2;
    public int _i;

    public Foo()
    {
        //Thread.Sleep(10);
        _initialized1 = true;
        Thread.Sleep(10);
        _initialized2 = true;
    }
}


Resharper заругался на Access to modified closure. Но checkInitializedThread все-равно видит новые Foo
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re[9]: Хороший вопрос
От: bl-blx Россия http://yegodm.blogspot.com
Дата: 03.07.13 12:47
Оценка:
Здравствуйте, igor-booch, Вы писали:

BB>>Вы уверены, что checkInitializedThread увидит значение foo, отличное от первого new Foo()?


IB>Хороший вопрос, Вы наверное имеете ввиду Access to modified closure.

IB>Проверил, нет, же, видит прекрасно новые Foo.
IB>И Resharper не ругается на Access to modified closure.

Нет, я просто не соображу, где там барьер, который гарантирует видимость другим потоком.
El pueblo unido jamás será vencido.
Re[10]: Хороший вопрос
От: Sinix  
Дата: 03.07.13 13:08
Оценка:
Здравствуйте, bl-blx, Вы писали:

BB>Нет, я просто не соображу, где там барьер, который гарантирует видимость другим потоком.


Под x86/x64 по-прежнему работает правило "all writes are volatile". Под арм/итаниум — с оговорками (см C# Memory Model Implementation on ARM).
Re[11]: Хороший вопрос
От: bl-blx Россия http://yegodm.blogspot.com
Дата: 03.07.13 13:17
Оценка: +2
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, bl-blx, Вы писали:


BB>>Нет, я просто не соображу, где там барьер, который гарантирует видимость другим потоком.


S>Под x86/x64 по-прежнему работает правило "all writes are volatile". Под арм/итаниум — с оговорками (см C# Memory Model Implementation on ARM).


Да, спасибо — забыл об этом, но, тем не менее, второй поток делает non-volatile read — с чего бы ему в main memory лезть?
El pueblo unido jamás será vencido.
Re[12]: Хороший вопрос
От: Sinix  
Дата: 03.07.13 13:25
Оценка: -1
Здравствуйте, bl-blx, Вы писали:

BB>Да, спасибо — забыл об этом, но, тем не менее, второй поток делает non-volatile read — с чего бы ему в main memory лезть?

Это нужно спрашивать у специалистов, если верить stackoverflow — под x86/64 обновление кэша берёт на себя сам процессор.
Re[7]: Стоит ли переносить инициализацию в отдельный метод?
От: abibok  
Дата: 03.07.13 14:59
Оценка:
S>Или просто посмотреть содержимое ex.InnerException

Вы не совсем понимаете глубину проблемы. В моей задаче InnerException не поможет.
Re[7]: Стоит ли переносить инициализацию в отдельный метод?
От: abibok  
Дата: 03.07.13 15:01
Оценка:
A>Есть несколько решений этой проблемы:
A>http://stackoverflow.com/questions/57383/in-c-how-can-i-rethrow-innerexception-without-losing-stack-trace

Для исключения в конструкторе это не подойдет.
Re[13]: Хороший вопрос
От: drol  
Дата: 03.07.13 15:32
Оценка: 20 (1)
Здравствуйте, Sinix, Вы писали:

S>под x86/64 обновление кэша берёт на себя сам процессор.


Кэш данных в архитектурах x86/x64 когерентен. И к обсуждаемым вопросам не имеет никакого отношения.
Re[12]: Хороший вопрос
От: drol  
Дата: 03.07.13 15:51
Оценка:
Здравствуйте, bl-blx, Вы писали:

BB>второй поток делает non-volatile read — с чего бы ему в main memory лезть?


Совершенно верно. В потоке checkInitializedThread и foo, и foo._initialized1, и foo._initialized2, и foo._i могут быть закэшированы. Сделать это способна любая фаза кодогенерации: IL, JIT и т.д.
Re[8]: Стоит ли переносить инициализацию в отдельный метод?
От: artelk  
Дата: 03.07.13 16:04
Оценка:
Здравствуйте, abibok, Вы писали:

A>>Есть несколько решений этой проблемы:

A>>http://stackoverflow.com/questions/57383/in-c-how-can-i-rethrow-innerexception-without-losing-stack-trace

A>Для исключения в конструкторе это не подойдет.

Ну тогда и твой вариант не подойдет

public static object CreateInstance(Type type, object[] arguments)
{
   try
   {
      Activator.CreateInstance(t, args);
   }
   catch(TargetInvocationException ex)
   {
     throw ex.InnerException.PreserveStackTrace();
   }
}

public static class ExceptionHelper
{
    private static readonly Action<Exception> _preserveInternalException;

    static ExceptionHelper()
    {
        var preserveStackTrace = typeof( Exception ).GetMethod( "InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic );
        _preserveInternalException = (Action<Exception>)Delegate.CreateDelegate( typeof( Action<Exception> ), preserveStackTrace );            
    }

    public static Exception PreserveStackTrace(this Exception ex )
    {
        _preserveInternalException( ex );
        return ex;
    }
}
Re[9]: Стоит ли переносить инициализацию в отдельный метод?
От: abibok  
Дата: 03.07.13 16:11
Оценка:
A>>Для исключения в конструкторе это не подойдет.
A>Ну тогда и твой вариант не подойдет

А мой как раз подойдет. Я плотно занимался этой темой, знаю кучу нюансов по поводу бросания и перехвата исключений, и этот код сейчас работает.
Re[10]: Стоит ли переносить инициализацию в отдельный метод?
От: artelk  
Дата: 03.07.13 16:17
Оценка:
Здравствуйте, abibok, Вы писали:

A>>>Для исключения в конструкторе это не подойдет.

A>>Ну тогда и твой вариант не подойдет

A>А мой как раз подойдет.

В чем будет отличие от моего?

A>Я плотно занимался этой темой, знаю кучу нюансов по поводу бросания и перехвата исключений, и этот код сейчас работает.

И на каждый вызов делает компиляцию создаваемого Expression-а?
Re[7]: Конструктор не бросает исключений, не связанных с проверкой аргументов
От: abibok  
Дата: 03.07.13 16:25
Оценка:
IB>А Initialize(...) тоже не может выкидывать исключения не связанные с проверкой аргументов?

Может, это же обычный метод. И его исключения не завернут в TargetInvocationException.

IB>Проверка аргументов понятие растяжимое.

IB>Она может быть любой сложности.

Здесь нет абсолютного решения — только конструктор или только отдельный метод. Нужно смотреть по ситуации. Я просто показал, что есть ситуации, когда наличие метода оправдано.
Re[11]: Стоит ли переносить инициализацию в отдельный метод?
От: abibok  
Дата: 03.07.13 16:33
Оценка:
A>>А мой как раз подойдет.
A>В чем будет отличие от моего?

В вашем случае (кстати, дайте пример вашего кода) исключение в конструкторе ловится внутри CLR, заворачивается в TargetInvocationException и перебрасывается. Все, куча информации потеряна. Например, значения локальных переменных в момент бросания уже не получить.

A>>Я плотно занимался этой темой, знаю кучу нюансов по поводу бросания и перехвата исключений, и этот код сейчас работает.

A>И на каждый вызов делает компиляцию создаваемого Expression-а?

Да. Это большая проблема? Мы не обязаны пользоваться этой функцией для создания абсолютно всех объектов. А там где это необходимо, потеря производительности (не такая уж и большая) оправдана.
Re[8]: Стоит ли переносить инициализацию в отдельный метод?
От: Sinix  
Дата: 03.07.13 17:18
Оценка:
Здравствуйте, abibok, Вы писали:

S>>Или просто посмотреть содержимое ex.InnerException


A>Вы не совсем понимаете глубину проблемы. В моей задаче InnerException не поможет.

Задачу вы пока не озвучили

Если как написано в другом посте, проблема в отладке — в настройках (debug-exceptions) можно включить прерывание отладки в момент броска исключения.
Re[12]: Стоит ли переносить инициализацию в отдельный метод?
От: artelk  
Дата: 03.07.13 17:30
Оценка:
Здравствуйте, abibok, Вы писали:

A>В вашем случае (кстати, дайте пример вашего кода) исключение в конструкторе ловится внутри CLR, заворачивается в TargetInvocationException и перебрасывается. Все, куча информации потеряна. Например, значения локальных переменных в момент бросания уже не получить.


О чем это ты, можно поподробнее?
Вот код:
  Скрытый текст
public static class ExceptionHelper
{
    private static readonly Action<Exception> _preserveInternalException;

    static ExceptionHelper()
    {
        var preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic);
        _preserveInternalException = (Action<Exception>)Delegate.CreateDelegate(typeof(Action<Exception>), preserveStackTrace);
    }

    public static Exception PreserveStackTrace(this Exception ex)
    {
        _preserveInternalException(ex);
        return ex;
    }
}

public class A
{
    public A(int x, string s)
    {
        throw new ArgumentException("s");
    }
}

static class Program
{
    public static object CreateInstance(Type type, object[] arguments)
    {
        try
        {
            return Activator.CreateInstance(type, arguments);
        }
        catch (TargetInvocationException ex)
        {
            throw ex.InnerException.PreserveStackTrace();
        }
    }

    private static void Main()
    {
        try
        {
            //var a = new A(42, "Hi");
            var a = CreateInstance(typeof(A), new object[] { 42, "Hi" });
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }
}


В консоль выводится:

System.ArgumentException: s
at ConsoleApplication46.A..ctor(Int32 x, String s) in d:\tmp\ConsoleApplication46\ConsoleApplication46\Program.cs:line 34
at ConsoleApplication46.Program.CreateInstance(Type type, Object[] arguments) in d:\tmp\ConsoleApplication46\ConsoleApplication46\Program.cs:line 56
at ConsoleApplication46.Program.Main() in d:\tmp\ConsoleApplication46\ConsoleApplication46\Program.cs:line 80



A>>>Я плотно занимался этой темой, знаю кучу нюансов по поводу бросания и перехвата исключений, и этот код сейчас работает.

A>>И на каждый вызов делает компиляцию создаваемого Expression-а?
A>Да. Это большая проблема? Мы не обязаны пользоваться этой функцией для создания абсолютно всех объектов. А там где это необходимо, потеря производительности (не такая уж и большая) оправдана.
А че б не закэшировать активаторы? Ну да фиг с ним...
Re[9]: Стоит ли переносить инициализацию в отдельный метод?
От: abibok  
Дата: 03.07.13 17:32
Оценка: 20 (1)
S>Задачу вы пока не озвучили

Задача — сохранить максимум информации о контексте приложения в момент возникновения исключительной ситуации для логирования и последующей отладки. Это включает в себя правильный callstack со всей информацией, включая значения локальных переменных и параметров функций в каждом фрейме, а не только текстовое представление стека вызовов. Для этого исключение нужно перехватывать сразу после того, как оно было брошено и до того, как оно войдет в блок catch.

S>Если как написано в другом посте, проблема в отладке — в настройках (debug-exceptions) можно включить прерывание отладки в момент броска исключения.


Речь идет не только об отладке, но и о штатной работе программы. Плюс такая настройка будет прерывать отладку для любых исключений, а нас интересуют только те, которые unhandled.
Re[13]: Стоит ли переносить инициализацию в отдельный метод?
От: abibok  
Дата: 03.07.13 17:48
Оценка:
A>О чем это ты, можно поподробнее?

Представьте себе, что конструктор не такой простой, а имеет сложную внутреннюю логику, которая зависит от состояния членов класса, параметров и значений локальных переменных.
Мы хотим всю эту информацию сохранить в лог, а разработчику предоставить managed dump, который будет указывать прямо на исходное исключение, а не на место, где это исключение поймали и перевыбросили.
Более того, мы хотим иметь не только стек вызовов, который привел к исключению — А(B(C())), но и историю выполнения — X->Y->Z->A(B(C())). И если исключение произошло из-за значения члена класса _myField, то проследить по истории до места, где _myField стал таким, и это место тоже включить в лог. А потом автоматически сделать root cause analysis "из-за проблемы в методе X у нас возникают исключения в C, T и Q".

Это общая задача, которая должна решаться для любого метода. Просто конструкторы немного особенные и создание объектов при таком подходе требуют специального обращения.
A>В консоль выводится:
A>

A>System.ArgumentException: s
A> at ConsoleApplication46.A..ctor(Int32 x, String s) in d:\tmp\ConsoleApplication46\ConsoleApplication46\Program.cs:line 34
A> at ConsoleApplication46.Program.CreateInstance(Type type, Object[] arguments) in d:\tmp\ConsoleApplication46\ConsoleApplication46\Program.cs:line 56
A> at ConsoleApplication46.Program.Main() in d:\tmp\ConsoleApplication46\ConsoleApplication46\Program.cs:line 80


Мне кажется или у вас с номерами строк не все в порядке?

A>А че б не закэшировать активаторы? Ну да фиг с ним...


Конечно, можно закэшировать.
Re[7]: UninitializedException
От: artelk  
Дата: 03.07.13 17:52
Оценка:
Здравствуйте, Sinix, Вы писали:

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


HL>>Если в какой-то момент времени выполняется конструктор объекта, это значит, что он очень даже достижим.


S>Необязательно.

S>http://ericlippert.com/2013/06/10/construction-destruction/

Я правильно понимаю, что в общем виде мы не можем обеспечить гарантию освобождения unmanaged ресурсов?
void F()
{
  new SomeClassWithUnmanagedResources();
}

Finalizer может закончить работу до того, как в конструкторе будут выделены и сохранены в полях unmanaged ресурсы, так?
Мне, как разработчику SomeClassWithUnmanagedResources, что делать, чтобы обеспечить гарантию их освобождения?
Re[10]: Стоит ли переносить инициализацию в отдельный метод?
От: artelk  
Дата: 03.07.13 17:57
Оценка:
Здравствуйте, abibok, Вы писали:

S>>Задачу вы пока не озвучили


A>Задача — сохранить максимум информации о контексте приложения в момент возникновения исключительной ситуации для логирования и последующей отладки. Это включает в себя правильный callstack со всей информацией, включая значения локальных переменных и параметров функций в каждом фрейме, а не только текстовое представление стека вызовов. Для этого исключение нужно перехватывать сразу после того, как оно было брошено и до того, как оно войдет в блок catch.


Это что-то фирменное. Можно показать?
Re[11]: Стоит ли переносить инициализацию в отдельный метод?
От: abibok  
Дата: 03.07.13 18:05
Оценка:
A>Это что-то фирменное. Можно показать?

Это очень круто в плане debuggability, позволяет избавиться от необходимости постоянно логировать всякие мелочи, работает прозрачно для существующего и нового кода.
Показать не могу.
Re[12]: Стоит ли переносить инициализацию в отдельный метод?
От: artelk  
Дата: 03.07.13 18:11
Оценка:
Здравствуйте, abibok, Вы писали:

A>>Это что-то фирменное. Можно показать?


A>Это очень круто в плане debuggability, позволяет избавиться от необходимости постоянно логировать всякие мелочи, работает прозрачно для существующего и нового кода.

A>Показать не могу.

hackability?
Забавно, что в качестве аргумента ты используешь то, что показать не можешь...
Re[13]: Стоит ли переносить инициализацию в отдельный метод?
От: abibok  
Дата: 03.07.13 18:21
Оценка:
A>hackability?

В том числе, когда это касается воровства контекста повышенных привилегий. Вызываем мы "некоторый" метод как юзер, а он внутри делает

public void Foo(Something x)
try
{
    GiveMeAdminPrivileges();
    Process(x);
}
finally
{
    RevertToUserPrivileges();
}


и думает, что все безопасно, привилегии не уйдут вызывающему. А мы подсунем такой x, который приведет к исключению, и перехватим его до входа в finally, и вернемся в свой код ненадого.

A>Забавно, что в качестве аргумента ты используешь то, что показать не можешь...


В качестве аргумента я описываю реальную задачу, пусть и не очень распространенную (что нормально для всякой достаточно сложной задачи), а вот реализацию показать не могу. Но это не отменяет существование задачи и проблемы.
Re[14]: Стоит ли переносить инициализацию в отдельный метод?
От: artelk  
Дата: 03.07.13 19:48
Оценка:
Здравствуйте, abibok, Вы писали:

A>>hackability?


A>В том числе, когда это касается воровства контекста повышенных привилегий. Вызываем мы "некоторый" метод как юзер, а он внутри делает


A>
A>public void Foo(Something x)
A>try
A>{
A>    GiveMeAdminPrivileges();
A>    Process(x);
A>}
A>finally
A>{
A>    RevertToUserPrivileges();
A>}
A>


A>и думает, что все безопасно, привилегии не уйдут вызывающему. А мы подсунем такой x, который приведет к исключению, и перехватим его до входа в finally, и вернемся в свой код ненадого.

Интересно. А если Foo лежит в подписанной сборке в GAC, тоже перехватите? А Code Access Security тоже умеете обходить?

A>>Забавно, что в качестве аргумента ты используешь то, что показать не можешь...

A>В качестве аргумента я описываю реальную задачу, пусть и не очень распространенную (что нормально для всякой достаточно сложной задачи), а вот реализацию показать не могу. Но это не отменяет существование задачи и проблемы.
Описание задачи и ее существование не может быть аргументом.
Re[15]: Стоит ли переносить инициализацию в отдельный метод?
От: abibok  
Дата: 03.07.13 20:38
Оценка:
A>Интересно. А если Foo лежит в подписанной сборке в GAC, тоже перехватите? А Code Access Security тоже умеете обходить?

Дайте пример кода, я попробую, чтобы не гадать. Подпись не должна влиять, мы не изменяем сборку.
Re[3]: UninitializedException
От: Sinclair Россия https://github.com/evilguest/
Дата: 04.07.13 04:23
Оценка: 4 (1) +2
Здравствуйте, drol, Вы писали:
D>Это верно только для однопоточной ситуации. В многопоточном случае возможны такие "чудеса", как выполнение конструктора параллельно с финализатором.
Чтобы финализатор начал выполняться параллельно с конструктором, нужно, чтобы поток исполнения конструктора дошёл до места, после которого нет обращений к this.
Это означает, что если у нас есть недоинициализированный экземпляр, то ссылка на него удерживается в рутах GC, и в лапы финализатора он не попадёт.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[14]: Хороший вопрос
От: Sinix  
Дата: 04.07.13 06:54
Оценка:
Здравствуйте, drol, Вы писали:


D>Кэш данных в архитектурах x86/x64 когерентен. И к обсуждаемым вопросам не имеет никакого отношения.


Не для холивара

Ок, а чем ещё можно объяснить, что пример igor-booch (чуть допилил) не падает?
    class Program
    {
        static Foo foo = new Foo(10000);
        static void Main(string[] args)
        {
            Thread instantiationThread = new Thread(() =>
            {
                do
                {
                    foo = new Foo(10000);
                } while (true);
            });

            Thread checkInitializedThread = new Thread(() =>
            {
                do
                {
                    var local = foo;
                    if (!local._initialized1 | !local._initialized2 | local._val == 0)
                    {
                        Console.WriteLine("Bingo!!!");
                    }
                } while (true);
            });

            instantiationThread.Start();
            checkInitializedThread.Start();
            Console.ReadKey();
        }
        
        class Foo
        {
            private static readonly Random rnd = new Random(0);

            public bool _initialized1;
            public bool _initialized2;
            public int _val;


            public Foo(int count)
            {
                //Thread.Sleep(10);
                _initialized1 = true;
                //Thread.Sleep(10);
                _initialized2 = true;

                _val = rnd.Next(count) + 1;
            }
        }
    }



В дизасме никакой магии нет.
  Скрытый текст
                    foo = new Foo(10000); (конструктор заинлайнен)
00000000  push        ebp  
00000001  mov         ebp,esp 
00000003  push        esi  
00000004  mov         ecx,193488h 
00000009  call        FFCE1E14 
0000000e  mov         esi,eax 
00000010  mov         byte ptr [esi+8],1 
00000014  mov         byte ptr [esi+9],1 
00000018  mov         ecx,dword ptr ds:[035E1F0Ch] 
0000001e  mov         edx,2710h 
00000023  mov         eax,dword ptr [ecx] 
00000025  call        dword ptr [eax+44h] 
00000028  inc         eax  
00000029  mov         dword ptr [esi+4],eax
0000002c  lea         edx,ds:[035E1F00h] 
00000032  call        72232DD0 
00000037  jmp         00000004


                    var local = foo;
00000000  push        ebp  
00000001  mov         ebp,esp 
00000003  mov         ecx,dword ptr ds:[035E1F00h]
Re[4]: UninitializedException
От: drol  
Дата: 04.07.13 07:16
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Чтобы финализатор начал выполняться параллельно с конструктором, нужно, чтобы поток исполнения конструктора дошёл до места, после которого нет обращений к this.


Ну да. В чём проблема-то ?

S>Это означает, что если у нас есть недоинициализированный экземпляр, то ссылка на него удерживается в рутах GC, и в лапы финализатора он не попадёт.


Ты как-то узко понимаешь инициализацию. Вот есть поле. Конструктор проставил в него ссылку на объект. А теперь вызывает какие-то методы этого объекта. С этого момента зависимости по this уже нет. Однако инициализация идёт во всю.
Re[15]: Стоит ли переносить инициализацию в отдельный метод?
От: Sinclair Россия https://github.com/evilguest/
Дата: 04.07.13 07:28
Оценка:
Здравствуйте, artelk, Вы писали:

A>>и думает, что все безопасно, привилегии не уйдут вызывающему. А мы подсунем такой x, который приведет к исключению, и перехватим его до входа в finally, и вернемся в свой код ненадого.

A>Интересно. А если Foo лежит в подписанной сборке в GAC, тоже перехватите? А Code Access Security тоже умеете обходить?
Ну конечно же да. Они же явно пользуются Debugger Services и шаманством с CLR Host, а там all bets are off.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[15]: Хороший вопрос
От: drol  
Дата: 04.07.13 07:52
Оценка: 20 (1)
Здравствуйте, Sinix, Вы писали:

S>Ок, а чем ещё можно объяснить, что пример igor-booch (чуть допилил) не падает?


Тем, что JIT у CLR очень недалёкий. Видно же в ассемблере, что чтение ссылки из памяти происходит на каждой итерации. Замените потроха checkInitializedThread на более простую конструкцию:

var local = foo;
do
{
} while (local==foo);

Console.WriteLine("local != foo");


...и всё сразу станет по-другому (при запуске Release-сборки вне Visual Studio).
Re[2]: Стоит ли переносить инициализацию в отдельный метод?
От: icWasya  
Дата: 04.07.13 09:01
Оценка:
Здравствуйте, HowardLovekraft, Вы писали:

HL>Здравствуйте, Аноним, Вы писали:


А>>Есть ли у этого подхода преимущества перед обычной инициализацией через конструктор в C#?

HL>Что имеется ввиду? Это:
HL>
HL>class MyClass
HL>{
HL>    private MyClass()
HL>    {
HL>    }    

HL>    public MyClass Create(){};
HL>}
HL>

HL>это:
HL>
HL>class MyClass()
HL>{
HL>    private void Initialize()
HL>    {
HL>    }

HL>    public MyClass()
HL>    {
HL>        Initialize();
HL>    }
HL>}
HL>

HL>или еще какой-то вариант?

class MyClass()
{
    private void Initialize(int IntParam,string Name)
    {
       /* 
         тут 100500 строк кода
       */
    }
    // а вот и конструкторы
    public MyClass()
    {
        Initialize(0,"Zero");
    }
    public MyClass(int IntParam)
    {
        Initialize(IntParam,"Int");
    }
    public MyClass(int IntParam,string Name)
    {
        Initialize(IntParam,Name)
    }

}
Re[11]: Стоит ли переносить инициализацию в отдельный метод?
От: vorona  
Дата: 04.07.13 10:02
Оценка:
http://www.rsdn.ru/forum/dotnet/4812371
Автор: abibok
Дата: 10.07.12
Re[3]: Стоит ли переносить инициализацию в отдельный метод?
От: Аноним  
Дата: 04.07.13 10:05
Оценка:
Tom>использовать static-методы вида CreateForA(), CreateForB().
Tom>Это до того момента когда вам тесты понадобится писать. В целом для нормально тестируемого кода надо забыть что такое статический метод, или статическая переменная

А причем тут тесты ? В чем подвох ?
Re[4]: UninitializedException
От: artelk  
Дата: 04.07.13 12:14
Оценка: 48 (2)
Здравствуйте, Sinclair, Вы писали:

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

D>>Это верно только для однопоточной ситуации. В многопоточном случае возможны такие "чудеса", как выполнение конструктора параллельно с финализатором.
S>Чтобы финализатор начал выполняться параллельно с конструктором, нужно, чтобы поток исполнения конструктора дошёл до места, после которого нет обращений к this.
S>Это означает, что если у нас есть недоинициализированный экземпляр, то ссылка на него удерживается в рутах GC, и в лапы финализатора он не попадёт.

Понятно. То есть все будет работать так, как будто финализатор работает после конструктора до тех пор, пока у финализатора не будет какого-нибудь хитрого побочного эффекта, влияющего на наблюдаемое поведение приложения (а не только освобождение занятых системных ресурсов).
Удалось такого добиться на основе глобального изменяемого состояния:

public class A
{
    public static int N;
    private int x;

    public A()
    {
        N = 42;

        GC.Collect();
        GC.WaitForPendingFinalizers();

        Console.WriteLine(N);
        //x = N; //uncomment me
    }

    ~A()
    {
        N = 666;
    }
}

static void Main()
{
    new A();

    Console.WriteLine("Done...");
    Console.ReadKey();

    return;
}


В релизе при запуске вне студии выводит 666. Если раскоментировать "x = N;" в последней строке конструктора, то выводит 42.
Re[5]: UninitializedException
От: Sinclair Россия https://github.com/evilguest/
Дата: 04.07.13 17:09
Оценка: -1
Здравствуйте, drol, Вы писали:

D>Ты как-то узко понимаешь инициализацию.


Вот есть поле. Конструктор проставил в него ссылку на объект. А теперь вызывает какие-то методы этого объекта. С этого момента зависимости по this уже нет. Однако инициализация идёт во всю.
Инициализация — это, по определению, изменение состояния объекта. Как только мы перестали менять состояние объекта — его инициализация завершилась.
Если финализатор объекта рассчитывает на какое-то определённое состояние другого объекта — это вина финализатора.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: Стоит ли переносить инициализацию в отдельный метод?
От: IB Австрия http://rsdn.ru
Дата: 05.07.13 08:24
Оценка:
Здравствуйте, Tom, Вы писали:

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

Ну, без фанатизма.
В данном случае, статический метод ничем не отличается от конструктора. Если у тебя код организован так, что экземпляр этого класса ты инжектишь, а не создаешь прямо в том классе, который хочешь оттестировать, то совершенно пофиг как ты его создаешь в боевом коде, через статический метод или честный конструктор, на тестировании это никак не скажется.
Мы уже победили, просто это еще не так заметно...
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.