Re[2]: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: zelenprog  
Дата: 25.08.23 08:10
Оценка:
Q>А старая добрая двухфазная инициализация не подойдет?

Q>См. как сделаны практически все нетривиальные классы в MFC: конструктор без параметров и функции Create, CreateEx, ... с параметрами.

Q>Примерно так:
Q>
Q>CWnd w;
Q>w.Create(NULL, "Hello");
Q>


Подойдет.

Q>В современном С++ считается скорее антипаттерном.


Ну вот это и смущает, что это антипаттерн.
Поэтому я и задал здесь вопрос: можно ли как-то добиться такого же результата только с помощью какого-либо "правильного" паттерна?
Re[2]: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: zelenprog  
Дата: 25.08.23 08:13
Оценка:
_>А нафига вам вообще конструктор. Получайте сразу готовый к работе класс, запрашивая его у посредника:
_>
_>  something=di.GetSomething()
_>


_>Смотри dependency inversion и factory. Они уже внутри если надо создают подходящий класс и настраивают его для работы.


А как посредник\factory будет создавать и настраивать "целевой" класс, если у этого "целевого" класса нету конструктора с параметрами?
Похожий ответ\вопрос вот в этой ветке прозвучал:
https://rsdn.org/forum/design/8586953.1
Автор: qaz77
Дата: 24.08.23
Re[2]: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: zelenprog  
Дата: 25.08.23 08:28
Оценка:
Z>Какую функцию выполняет пустой нефункциональный объект между шагами 1 и 2?

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

Z>Если собирает данные конфигурации...


Этот объект не собирает данные. Он ждет пока эти данные будут ему переданы извне.
Так как не хотелось бы, чтобы этот класс знал больше, чем ему нужно.

Z>... то SRPшнее выделить их в отдельный объект, который потом передадим в конструктор/фабричный метод, который и выполнит захват ресурсов.


Да, так правильнее.
Но у конструктора нету параметров
Re[2]: "Гибкий" конструктор для инициализации объекта класса (а
От: zelenprog  
Дата: 25.08.23 08:32
Оценка:
S>Чтож, вычислили параметры, закинули в конструктор / или там в метод init()

А что за метод init()?

Предлагаете делать у каждого класса такой метод?
Напомню: у конструктора нету параметров.
Re[3]: Builder
От: sergii.p  
Дата: 25.08.23 12:46
Оценка: :))
Здравствуйте, zelenprog, Вы писали:

Z>Получается, для каждого класса нужен еще один дополнительный класс Builder?


можно и без дополнительного класса

class Test
{
   string mFileName;
   File mFile;

   Test Set_FileName(pFileName)
   {
      Test res = *this;
      res.mFileName = pFileName;
      return res;
   }

   void Init ()
   {
      mFile = FileOpen(mFileName);
   }
};

int main() {
    Test t = Test().Set_FileName("foo.txt").Init();
}
Re[3]: "Гибкий" конструктор для инициализации объекта класса
От: Sm0ke Россия ksi
Дата: 25.08.23 13:04
Оценка:
Здравствуйте, zelenprog, Вы писали:


S>>Чтож, вычислили параметры, закинули в конструктор / или там в метод init()


Z>А что за метод init()?


Z>Предлагаете делать у каждого класса такой метод?

Z>Напомню: у конструктора нету параметров.

Каждому классу, требующему инициализацию, исходя из параметров
можно сделал метод init(params), раз конструктор без параметров.



Или: Про фабрику вам уже советовали

это Можно сделать так:

class Car ( int wheels, float max_speed )

class Test ( string path, file handle )

// фабрика
class maker ()

maker::new_car(int wheels, float max_speed)
{
  ret = new Car;
  ret.wheels = wheels;
  ret.max_speed = max_speed;
  return ret;
}

maker::new_test(string path)
{
  ret = new Test;
  ret.path = path;
  ret.handle = CreateFile(path);
  return ret;
}


Что тут происходит? Это класс maker, в котором несколько методов для создания целевых объектов. Фабричный метод создаёт, инициализирует и возвращает готовый экземпляр требуемого типа.

Не все языки позволяют выносить методы отдельно за пределы class def
// фабрика
class maker (

  Car new_car(int wheels, float max_speed)
  {
    ret = new Car;
    ret.wheels = wheels;
    ret.max_speed = max_speed;
    return ret;
  }

  Test new_test(string path)
  {
    ret = new Test;
    ret.path = path;
    ret.handle = CreateFile(path);
    return ret;
  }
)




кст. В вашем языке можно передать и хранить тип данных как значение?
Отредактировано 25.08.2023 13:21 Sm0ke . Предыдущая версия . Еще …
Отредактировано 25.08.2023 13:17 Sm0ke . Предыдущая версия .
Re[4]: "Гибкий" конструктор для инициализации объекта класса
От: zelenprog  
Дата: 25.08.23 14:13
Оценка:
S>кст. В вашем языке можно передать и хранить тип данных как значение?

Можно тип данных хранить в виде строки. Например:

lStr_ClassName = "T_Test";
lObj = CreateObject(lStr_ClassName);
Re[3]: "Гибкий" конструктор для инициализации объекта класса
От: m2user  
Дата: 26.08.23 06:57
Оценка: +1
Z>Статических методов нету в "моем" скриптовом языке.
Z>В этом скриптовом языке "урезанное" ООП. Все классы могут иметь только один конструктор без параметров.

ясно.

Z>lazy initialization скорее всего подходит.

Z>Но ведь это же плохой шаблон?

Почему?
Он просто иногда сложнее реализуем, например когда класс должен быть thread-safe.

Z>По сути мне надо сделать сто-то типа этого:


Что будет если вызвать Set_FileName после вызова Init?
По-видимому ObjectInitException (по аналогии с ObjectDisposedException)...

Выглядит не очень.
Подход с классами builder`ами выглядит более прозрачным и понятным.
Классов будет больше, но при этом методов в сумме примерно столько же.
Re[2]: "Гибкий" конструктор для инициализации объекта класса (а
От: Sm0ke Россия ksi
Дата: 26.08.23 15:43
Оценка:
Здравствуйте, Sm0ke, Вы писали:

Подумал ещё. Объединять параметры для создания ресурса в структуру похоже совет не очень, раз конструктор не может их принимать.
Думаю лучше сделать фабрику, которая из параметров выдаёт сразу готовый ресурс.

Не обязательно кстати на каждый класс заводить отдельный класс фабрики. Одна фабрика может иметь несколько разных фабричных методов, для целевых классов — объединённых по смыслу.
Re[4]: "Гибкий" конструктор для инициализации объекта класса
От: zelenprog  
Дата: 29.08.23 05:34
Оценка:
M>Что будет если вызвать Set_FileName после вызова Init?
M>По-видимому ObjectInitException (по аналогии с ObjectDisposedException)...
M>Выглядит не очень.

Так ведь если применить шаблон lazy initialization, то получится то же самое.
Разве нет?
Re[5]: "Гибкий" конструктор для инициализации объекта класса
От: m2user  
Дата: 29.08.23 07:11
Оценка:
Z>Так ведь если применить шаблон lazy initialization, то получится то же самое.
Z>Разве нет?

Отложенная инициализация это не про передачу параметров, а про то, когда и при каких условиях происходит "дорогая" (ресурсоемкая) операция.
Из стартового сообщения у меня сложилось впечатление, что задача состоит в том, чтобы ресурсоемкоую операцию (а не передачу параметров) убрать из конструктора.
После Ваших пояснений про беспараметровый конструктор и статические методы, я полагаю, что lazy initialization в приведенном Вами примере (class Test) можно применить разве что для того, чтобы убрать метод Init (или сделать его вызов опциональным).

К своему предыдущему комментарию про builder добавлю, что методы все-таки будут дублироваться, при этом я бы возвращал из билдера объект базового класса (или интерфейса) без Set/Init-методов.
Re[3]: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: Ziaw Россия  
Дата: 29.08.23 13:40
Оценка:
Здравствуйте, zelenprog, Вы писали:

Z>Этот объект не собирает данные. Он ждет пока эти данные будут ему переданы извне.

Z>Так как не хотелось бы, чтобы этот класс знал больше, чем ему нужно.

Не увидел противоречия. Активно он собирает или пассивно, его функция в это время — сбор данных в одну сущность для последующего применения.

Z>Да, так правильнее.

Z>Но у конструктора нету параметров

Если у конструктора не бывает параметров, то какой же это конструктор в общем случае?

Можно извратиться и инвертировать передачу параметров, договорившись о месте, откуда конструктор их возьмет и сможет выполнить функцию инициализации объекта. Но если в языке заложена невозможность полной инициализации объекта, то скорее всего есть общепринятые альтернативные паттерны инициализации.
Re: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: zelenprog  
Дата: 31.08.23 05:43
Оценка:
Z> ... бывают такие ситуации, когда конструктор не всегда может выделить необходимые ресурсы.
Z>Например, в момент создания объекта нет достаточной информации для "полноценного" создания объекта.
Z>Поэтому такие объекты приходится создавать в следующем порядке:
Z>1) Сначала нужно создать "пустой" объект, вызвать его конструктор. Объект создается, но пока не "захватывает" ресурсы. В таком состоянии объект не функционален.
Z>2) Затем нужно передать этому объекту необходимую информацию (установить значения его "ключевых" свойств), от которой зависит логика "захвата" ресурсов.
Z>3) И только теперь можно вызвать метод объекта, который выполнит захват ресурсов, назовем его InitializeResources().

Z>... у меня сейчас есть ограничение, чтобы конструкторы классов были вообще без параметров.

Z>Получается, нужен какой-то "шаблон" для выделения ресурсов, аналогичный по схеме IDisposable, но противоположный по значению.
Z>Есть ли какие-либо шаблоны\методики\практики более "гибкого" выделения ресурсов в дополнение к конструктору класса?

Думаю лучше обсуждать на примере.
Вот упрощенный пример описываемой проблемы.

Простая программа читает список товаров и отображает их на форме.
Программа умеет работать с БД двух типов, у которых немного отличается структура и наименования полей. Соответственно для каждого типа БД нужен свой Адаптер.
Класс DBGoodsReader ("Читатель") не зависит от конкретной БД, он реализует общий алгоритм чтения товаров: сначала с помощью Адаптера извлекает данные, затем выполняет их некоторые преобразования ("подготовку" для отображения).

Получается, алгоритм такой:
1) Определяем тип БД
2) Создаем Адаптер, соответствующий типу БД
3) Передаем этот Адаптер классу "Читатель" (DBGoodsReader)
4) Читаем и отображаем данные.

Проблема в том, что конструкторы не

Class MainForm
{
    string mFileName;
    
    void OnButton_ReadDatabase()
    {
        T_DatabaseInfo lDatabaseInfo = new T_DatabaseInfo(mFileName);
        T_DBGoodsReader lDBGoodsReader = new T_DBGoodsReader;
        I_DBAdapter lDBAdapter = null;
        
        if lDatabaseInfo.DBType = Database.DBType1 then
            lDBAdapter = new T_DBAdapter1(mFileName);
        elseif lDatabaseInfo.DBType = Database.DBType2
            lDBAdapter = new T_DBAdapter2(mFileName);
        else
            // исключение "неизвестный тип БД
        endif
        
        lDBGoodsReader.SetAdapter(lAdapter);
        lDBGoodsReader.Init();
        lDBGoodsReader.Read();
        
        // показать на форме список товаров
    }
}


Напомню, что в конструктор невозможно передать параметры.
Приходится для "Читателя" cделать метод "SetAdapter", чтобы передать ему конкретный объект "Адаптер". И затем вызвать метод "Init".
Я читал, что это анти-паттерн. Но как сделать по другому?

Второй вопрос. Кто (какой объект) должен отвечать за проверку типа базы и создание конкретного Адаптера?
Сейчас это делается в Форме. Но по "теории" форма не должна ничего знать о таких "подробностях". Она должна просто использовать "Читателя".
Re[6]: "Гибкий" конструктор для инициализации объекта класса
От: zelenprog  
Дата: 31.08.23 05:54
Оценка:
M>Отложенная инициализация это не про передачу параметров, а про то, когда и при каких условиях происходит "дорогая" (ресурсоемкая) операция.

ОК.

M>Из стартового сообщения у меня сложилось впечатление, что задача состоит в том, чтобы ресурсоемкоую операцию (а не передачу параметров) убрать из конструктора.


Задача в том, чтобы класс выполнил "захват" ресурсов, необходимых для его функционирования, с учетом того, что невозможно передать необходимую для этого информацию через параметры конструктора.

M>После Ваших пояснений про беспараметровый конструктор и статические методы, я полагаю, что lazy initialization в приведенном Вами примере (class Test) можно применить разве что для того, чтобы убрать метод Init (или сделать его вызов опциональным).

M>К своему предыдущему комментарию про builder добавлю, что методы все-таки будут дублироваться, при этом я бы возвращал из билдера объект базового класса (или интерфейса) без Set/Init-методов.

Прошу перейти к обсуждению этой проблемы на примере.
Я описал пример вот в этом сообщении:
https://rsdn.org/forum/design/8590957.1
Автор: zelenprog
Дата: 31.08.23
Re[4]: Builder
От: Sinclair Россия https://github.com/evilguest/
Дата: 05.09.23 07:42
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>можно и без дополнительного класса

Можно, но нельзя:

class Test
{
   string mFileName;
   File mFile;

   Test Set_FileName(pFileName)
   {
      Test res = *this;
      res.mFileName = pFileName;
      return res;
   }

   void Init ()
   {
      mFile = FileOpen(mFileName);
   }
   Test Init_Corrected ()
   {
      Test res = *this;
      res.mFile = FileOpen(mFileName);
      return res;
   }
   int Read(byte* dest, int count)
   {
      return FileRead(mFile, dest, count);
   }
};

int main() {
   Test t1 = Test().Set_FileName("foo.txt").Init(); // compile error: cannot convert expression of type 'void' to type 'Test'
   Test t2 = Test();
   Test t3 = Test().Set_FileName("foo.txt");
   Test t4 = Test().Set_FileName("foo.txt").Init_Corrected(); 
   byte* buffer = new byte[1024];
   t2.Read(buffer, 1024); // oops
   t3.Read(buffer, 1024); // oops
   t4.Read(buffer, 1024); // :)
}
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: kov_serg Россия  
Дата: 05.09.23 10:50
Оценка:
Здравствуйте, zelenprog, Вы писали:

Z>Думаю лучше обсуждать на примере.

Z>Вот упрощенный пример описываемой проблемы.

Z>Простая программа читает список товаров и отображает их на форме.

Z>Программа умеет работать с БД двух типов, у которых немного отличается структура и наименования полей. Соответственно для каждого типа БД нужен свой Адаптер.
Z>Класс DBGoodsReader ("Читатель") не зависит от конкретной БД, он реализует общий алгоритм чтения товаров: сначала с помощью Адаптера извлекает данные, затем выполняет их некоторые преобразования ("подготовку" для отображения).

Z>Получается, алгоритм такой:

Z>1) Определяем тип БД
Z>2) Создаем Адаптер, соответствующий типу БД
Z>3) Передаем этот Адаптер классу "Читатель" (DBGoodsReader)
Z>4) Читаем и отображаем данные.

Z>Проблема в том, что конструкторы не


Z>
Z>Class MainForm
Z>{
Z>    string mFileName;
    
Z>    void OnButton_ReadDatabase()
Z>    {
Z>        T_DatabaseInfo lDatabaseInfo = new T_DatabaseInfo(mFileName);
Z>        T_DBGoodsReader lDBGoodsReader = new T_DBGoodsReader;
Z>        I_DBAdapter lDBAdapter = null;
        
Z>        if lDatabaseInfo.DBType = Database.DBType1 then
Z>            lDBAdapter = new T_DBAdapter1(mFileName);
Z>        elseif lDatabaseInfo.DBType = Database.DBType2
Z>            lDBAdapter = new T_DBAdapter2(mFileName);
Z>        else
Z>            // исключение "неизвестный тип БД
Z>        endif
        
Z>        lDBGoodsReader.SetAdapter(lAdapter);
Z>        lDBGoodsReader.Init();
Z>        lDBGoodsReader.Read();
        
Z>        // показать на форме список товаров
Z>    }
Z>}
Z>


Z>Напомню, что в конструктор невозможно передать параметры.

Z>Приходится для "Читателя" cделать метод "SetAdapter", чтобы передать ему конкретный объект "Адаптер". И затем вызвать метод "Init".
Z>Я читал, что это анти-паттерн. Но как сделать по другому?

Z>Второй вопрос. Кто (какой объект) должен отвечать за проверку типа базы и создание конкретного Адаптера?

Z>Сейчас это делается в Форме. Но по "теории" форма не должна ничего знать о таких "подробностях". Она должна просто использовать "Читателя".

Так и что вам мешает?
    public partial class Form1 : Form {
        Resolve di = new Resolve1();
        public Form1() {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e) {
            comboBox1.SelectedIndex = 0;
        }
        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) {
            int num=comboBox1.SelectedIndex+1;
            di.GetConfig().SelectSource(num);
            update_goods();
        }
        void update_goods() {
            var reader=di.GetGoods();
            var lv = listView1;
            lv.BeginUpdate();
            lv.Items.Clear();
            Goods item;
            while(reader.Next(out item)) {
                var li = new ListViewItem();
                li.Text = item.name;
                li.SubItems.Add(String.Format("{0}", item.amount));
                li.SubItems.Add(String.Format("{0:F2}", item.price));
                lv.Items.Add(li);
            }
            lv.EndUpdate();
        }
    }

    class Goods {
        public string name = "";
        public double amount = 0;
        public double price = 0;
    };
    interface GoodsReader {
        bool Next(out Goods item);
    };
    interface Config {
        void SelectSource(int num);
    }
    interface Resolve {
        GoodsReader GetGoods();
        Config GetConfig();
    }

  Скрытый текст

    class GoodsReader1 : GoodsReader {
        int index = 0;
        Goods[] list = new Goods[]{
            new Goods() { name="good1", amount=4, price=100 },
            new Goods() { name="good2", amount=2, price=150 },
            new Goods() { name="good3", amount=1, price= 75 }
        };
        public bool Next(out Goods item) {
            if (index < 0 || index >= list.Length) { item = null; return false; }
            item = list[index++];
            return true;
        }
    }
    class GoodsReader2 : GoodsReader {
        int index = 0;
        Goods[] list = new Goods[]{
            new Goods() { name="goodA", amount=40, price=120 },
            new Goods() { name="goodB", amount=12, price=130 },
            new Goods() { name="goodC", amount=11, price= 80 },
            new Goods() { name="goodE", amount= 1, price= 30 }
        };
        public bool Next(out Goods item) {
            if (index < 0 || index >= list.Length) { item = null; return false; }
            item = list[index++];
            return true;
        }
    }
    class Config1 : Config {
        Action<int> cb;
        public void SelectSource(int num) { if (cb != null) cb(num); }
        public Config1(Action<int> cb) { this.cb = cb; }
    }

    class Resolve1 : Resolve {
        int source = 1;
        public Config GetConfig() { return new Config1(num => source = num); }
        public GoodsReader GetGoods() {
            if (source == 1) return new GoodsReader1();
            if (source == 2) return new GoodsReader2();
            throw new Exception("invalid data source");
        }

    }

  Скрытый текст
    partial class Form1 {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing) {
            if (disposing && (components != null)) {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent() {
            this.comboBox1 = new System.Windows.Forms.ComboBox();
            this.listView1 = new System.Windows.Forms.ListView();
            this.ColName = new System.Windows.Forms.ColumnHeader();
            this.ColAmount = new System.Windows.Forms.ColumnHeader();
            this.ColPrice = new System.Windows.Forms.ColumnHeader();
            this.SuspendLayout();
            // 
            // comboBox1
            // 
            this.comboBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
            this.comboBox1.FormattingEnabled = true;
            this.comboBox1.Items.AddRange(new object[] {
            "Source 1",
            "Source 2"});
            this.comboBox1.Location = new System.Drawing.Point(12, 12);
            this.comboBox1.Name = "comboBox1";
            this.comboBox1.Size = new System.Drawing.Size(260, 21);
            this.comboBox1.TabIndex = 0;
            this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged);
            // 
            // listView1
            // 
            this.listView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                        | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
            this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
            this.ColName,
            this.ColAmount,
            this.ColPrice});
            this.listView1.Location = new System.Drawing.Point(12, 48);
            this.listView1.Name = "listView1";
            this.listView1.Size = new System.Drawing.Size(260, 202);
            this.listView1.TabIndex = 1;
            this.listView1.UseCompatibleStateImageBehavior = false;
            this.listView1.View = System.Windows.Forms.View.Details;
            // 
            // ColName
            // 
            this.ColName.Text = "Name";
            // 
            // ColAmount
            // 
            this.ColAmount.Text = "Amount";
            // 
            // ColPrice
            // 
            this.ColPrice.Text = "Price";
            this.ColPrice.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(284, 262);
            this.Controls.Add(this.listView1);
            this.Controls.Add(this.comboBox1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.Load += new System.EventHandler(this.Form1_Load);
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.ComboBox comboBox1;
        private System.Windows.Forms.ListView listView1;
        private System.Windows.Forms.ColumnHeader ColName;
        private System.Windows.Forms.ColumnHeader ColAmount;
        private System.Windows.Forms.ColumnHeader ColPrice;
    }
Re[3]: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: zelenprog  
Дата: 07.09.23 06:17
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>


Спасибо за такой развернутый ответ!

_>Так и что вам мешает?


Наверно, недостаток опыта и знаний.


_>
_>    public partial class Form1 : Form {
_>        Resolve di = new Resolve1();
_>        public Form1() {
_>            InitializeComponent();
_>        }
...
_>


А разве "хорошо", то что форма знает о конкретном классе "Resolve1"?
Мне кажется, так не должно быть. Разве форма не должна быть "независимой" от конкретной реализации интерфейса Resolve?
Если да, то где создавать объект Resolve1? Может быть в функции Main?
Re[3]: "Гибкий" конструктор для инициализации объекта класса
От: zelenprog  
Дата: 07.09.23 06:21
Оценка:
Здравствуйте, kov_serg!

А можете еще подсказать как "исправить" вот этот код?

Надо сделать примерно следующее:
class Test
{
   string mFileName;
   File mFile;

   void Set_FileName (pFileName)
   {
      mFileName = pFileName;
   }

   void Init ()
   {
      mFile = FileOpen(mFileName);
   }
};

lTest = new Test;
lTest.Set_FileName("c:\test_file.txt")
lTest.Init();


Как здесь "правильно" выполнить захват ресурсов без метода Init()?
Напомню, что конструктор не может иметь параметров.
Отредактировано 07.09.2023 6:21 zelenprog . Предыдущая версия .
Re[4]: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: kov_serg Россия  
Дата: 08.09.23 12:09
Оценка:
Здравствуйте, zelenprog, Вы писали:

Z>А разве "хорошо", то что форма знает о конкретном классе "Resolve1"?

Z>Мне кажется, так не должно быть. Разве форма не должна быть "независимой" от конкретной реализации интерфейса Resolve?
Z>Если да, то где создавать объект Resolve1? Может быть в функции Main?
Видимо у вас просто фантазии не хватает пока. Обычно фомру тоже создаёт di
public Form1 GetForm1() {
  var form=new Form1()
  form.di=this;
  // еще чего-нибудь подкручиваем
  return form;
}
Re[4]: "Гибкий" конструктор для инициализации объекта класса
От: kov_serg Россия  
Дата: 08.09.23 12:15
Оценка:
Здравствуйте, zelenprog, Вы писали:

Z>А можете еще подсказать как "исправить" вот этот код?


Z>Как здесь "правильно" выполнить захват ресурсов без метода Init()?

1. Унаследоваться от IDisposable (что бы можно было использовать using)
2. Lazy init. Открывать файл по требованию.
3. Пересмотреть логику использования
   using(file=helper.OpenFile(filename)) {
     ...
   }


Z>Напомню, что конструктор не может иметь параметров.

И кому это мешает? Что мешает объекту иметь разные состояния, например с файлом и без файла.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.