using System;
public class BaseClass
{
// объявили строковое полеpublic string MyStr;
public BaseClass()
{
// в конструкторе используем объявленное выше поле
System.Console.WriteLine(MyStr);
}
}
public class Hello : BaseClass
{
// тут я хочу присвоить значение полю базового классаbase.MyStr = "Hello";
public Hello():base(){}
}
Понятно, что этот пример даже не компилится. Он должен только пояснить суть вопроса.
Есть базовый класс, поля которого используются в конструкторе, а значения полей задаются в производных классах. Не подскажите, как это правильно делается?
Здравствуйте, днс, Вы писали:
днс>Понятно, что этот пример даже не компилится. Он должен только пояснить суть вопроса. днс>Есть базовый класс, поля которого используются в конструкторе, а значения полей задаются в производных классах. Не подскажите, как это правильно делается?
Виртуальными свойствами или методами. В шарпе их можно вызывать из конструктора.
Здравствуйте, днс, Вы писали:
днс>... днс>Есть базовый класс, поля которого используются в конструкторе, а значения полей задаются в производных классах. Не подскажите, как это правильно делается?
public class BaseClass
{
public string MyStr;
public BaseClass(string str)
{
MyStr = str;
System.Console.WriteLine(MyStr);
}
}
public class Hello : BaseClass
{
public Hello() : base("Hello"){}
}
--
Справедливость выше закона. А человечность выше справедливости.
R>public class Hello : BaseClass
R>{
R> public Hello() : base("Hello"){}
R>}
R>
Применимо к конкретному примеру, но плохо подходит, если вместо одного поля у нас имеется десяток структур.
Однако, всеравно спасибо, за участие в обсуждении.
Здравствуйте, vmpire, Вы писали:
V>Виртуальными свойствами или методами. В шарпе их можно вызывать из конструктора.
Спасибо, работает. Правда немножко смущает оформление в производных классах -- слишком громоздко получается,
но это, возможно, с непривычки. Однако подождем еще, вдруг предложат что-то неожиданное
Здравствуйте, vmpire, Вы писали:
V>Здравствуйте, днс, Вы писали:
днс>>Понятно, что этот пример даже не компилится. Он должен только пояснить суть вопроса. днс>>Есть базовый класс, поля которого используются в конструкторе, а значения полей задаются в производных классах. Не подскажите, как это правильно делается?
V>Виртуальными свойствами или методами. В шарпе их можно вызывать из конструктора.
Можно, но не нужно. Безопасно это можно сделать только в конструкторе sealed класса.
Здравствуйте, samius, Вы писали:
V>>Виртуальными свойствами или методами. В шарпе их можно вызывать из конструктора. S>Можно, но не нужно. Безопасно это можно сделать только в конструкторе sealed класса.
Да, вызывать виртуальные методы потомков sealed-класса — это круто
А что значит "безопасно"? Поля производного класса не будут проинициализированы в его конструкторе? Да, не будут и при написании вызовов виртуальных методов из конструктора это нужно учитывать. Но той опасности, которая была в C++ тут не будет согласно стандарту языка C#.
Здравствуйте, vmpire, Вы писали:
V>Здравствуйте, samius, Вы писали:
V>>>Виртуальными свойствами или методами. В шарпе их можно вызывать из конструктора. S>>Можно, но не нужно. Безопасно это можно сделать только в конструкторе sealed класса. V>Да, вызывать виртуальные методы потомков sealed-класса — это круто
Про потомков я не говорил ни слова
V>А что значит "безопасно"? Поля производного класса не будут проинициализированы в его конструкторе? Да, не будут и при написании вызовов виртуальных методов из конструктора это нужно учитывать. Но той опасности, которая была в C++ тут не будет согласно стандарту языка C#.
Вот и получается, что когда из конструктора базового класса вызываются виртуальные методы, то производные классы обрастают дополнительными контрактами по поводу того, как именно им нужно перекрывать виртуальные методы, к чему можно обращаться, а к чему — нет. И речь не только о полях производного класса, а и о всех его методах, которые могут обращаться к полям. Все эти методы будут работать во время конструктора не так, как они будут работать после вызова конструктора производного класса.
О специфике виртуальных методов, вызываемых из конструктора базового класса, надо помнить не только при написании производных классов, а так же при рефакторинге. И если у меня в проекте 15Мб исходников, то мне помнить о таких дополнительных соглашениях некоторых классов довольно проблематично. А делать себе и коллегам пометки в коде "если обратишься к полю — ССЗБ" как-то несолидно.
Здравствуйте, днс, Вы писали:
днс>Здравствуйте, samius, Вы написали много интересного и, наверное, правильного, но по существу: у вас есть другое решение проблемы?
Проблема мной до конца не понятна.
Если производный класс создается лишь для того, чтобы задать какие-то значения полям базового класса, то наследование тут вообще не лучший выход. Вместо наследования следует применить производящие методы, например так:
public static BaseClass CreateHello()
{
return new BaseClass("Hello");
}
Если производный класс как-то расширяет базовый, то я бы избрал путь, который посоветовал rg45.
Смущает десяток структур — группируйте их в другие классы, либо разбивайте базовый на части.
Еще разок пересмотрев архитектуру приложения, склонился к методу, предложенному rg45, который сначала не оценил.
Был не прав. Всем спасибо. Считаю вопрос исчерпан.
Здравствуйте, samius, Вы писали:
V>>Да, вызывать виртуальные методы потомков sealed-класса — это круто S>Про потомков я не говорил ни слова
Про потомков было в изначальном вопросе
V>>А что значит "безопасно"? Поля производного класса не будут проинициализированы в его конструкторе? Да, не будут и при написании вызовов виртуальных методов из конструктора это нужно учитывать. Но той опасности, которая была в C++ тут не будет согласно стандарту языка C#. S>Вот и получается, что когда из конструктора базового класса вызываются виртуальные методы, то производные классы обрастают дополнительными контрактами по поводу того, как именно им нужно перекрывать виртуальные методы, к чему можно обращаться, а к чему — нет. И речь не только о полях производного класса, а и о всех его методах, которые могут обращаться к полям. Все эти методы будут работать во время конструктора не так, как они будут работать после вызова конструктора производного класса.
Замените в своих словах "виртуальные методы производных классов" на "невиртуальные методы того же класса", истинность высказывания не изменится. В любом случае программист должен следить, чтобы не было ошибочных обращений к неинициализированным переменным. Вызов именно виртуальных методов практически ничего к этой опасности не добавляет.
Наплодить ошибок можно в любой системе. А в вопросе речь шла о сравнительно простой логике, которой поля класса вообще не нужны.
Как жалко, что в C# нет модификатора параметров const как в C++
S>О специфике виртуальных методов, вызываемых из конструктора базового класса, надо помнить не только при написании производных классов, а так же при рефакторинге. И если у меня в проекте 15Мб исходников, то мне помнить о таких дополнительных соглашениях некоторых классов довольно проблематично. А делать себе и коллегам пометки в коде "если обратишься к полю — ССЗБ" как-то несолидно.
Документировать нетривиальные места кода теперь стало несолидно? Я что-то упустил в этой жизни?
Здравствуйте, vmpire, Вы писали:
V>Замените в своих словах "виртуальные методы производных классов" на "невиртуальные методы того же класса", истинность высказывания не изменится. В любом случае программист должен следить, чтобы не было ошибочных обращений к неинициализированным переменным. Вызов именно виртуальных методов практически ничего к этой опасности не добавляет.
вызов виртуального метода из конструктора базового класса усугубляет положение тем, что разработчик производного класса не контроллирует последовательность инициализации. Для меня это "практически ничего" довольно веский факт.
V>Наплодить ошибок можно в любой системе. А в вопросе речь шла о сравнительно простой логике, которой поля класса вообще не нужны. V>Как жалко, что в C# нет модификатора параметров const как в C++
Что бы это изменило в данном аспекте? Как бы повлияло на инициализацию полей?
S>>О специфике виртуальных методов, вызываемых из конструктора базового класса, надо помнить не только при написании производных классов, а так же при рефакторинге. И если у меня в проекте 15Мб исходников, то мне помнить о таких дополнительных соглашениях некоторых классов довольно проблематично. А делать себе и коллегам пометки в коде "если обратишься к полю — ССЗБ" как-то несолидно. V>Документировать нетривиальные места кода теперь стало несолидно? Я что-то упустил в этой жизни?
Предпочитаю не вносить нетривиальные места, чем их документировать.
Благо, альтернатива такому решению есть.
Здравствуйте, samius, Вы писали:
V>>Наплодить ошибок можно в любой системе. А в вопросе речь шла о сравнительно простой логике, которой поля класса вообще не нужны. V>>Как жалко, что в C# нет модификатора параметров const как в C++ S>Что бы это изменило в данном аспекте? Как бы повлияло на инициализацию полей?
Можно было бы хотя бы застраховаться от изменений полей класса такими методами.
Я согласен, что решение не очень изящное, но принципиальных причин не использовать его не вижу. В ряде случаев оно можжет упростить жизнь.
Здравствуйте, vmpire, Вы писали:
V>Здравствуйте, samius, Вы писали:
V>Я согласен, что решение не очень изящное, но принципиальных причин не использовать его не вижу. В ряде случаев оно можжет упростить жизнь.
Если методы тривиальные и они призваны лишь поставлять значение базовому классу (а не вычислять его), то этими методами можно вполне пользоваться _вместо_ полей базового класса.
Если требуются какие-то вычисления и кэширование полученных значений в полях базового класса — можно использовать отложенную инициализацию (с проверками при обращении к свойствам базового класса), например.
Можно вместо полей базового класса использовать абстрактные свойства, а производные классы будут хранить значения в _своих_ полях.
На худой конец, можно сделать в базовом классе защищенный метод Initialize(), возможно виртуальный, и наградить все финишные классы в цепочке наследования обязанностью вызвать его в своем конструкторе.
Все это я предпочту вызовам виртуальных методов из конструктора базового класса. Однако допускаю ряд случаев, когда никакие другие решения не подойдут по каким-либо причинам. В таком случае придется использовать это решение. Табу на него у меня нет
Здравствуйте, samius, Вы писали:
V>>Я согласен, что решение не очень изящное, но принципиальных причин не использовать его не вижу. В ряде случаев оно можжет упростить жизнь.
S>Если методы тривиальные и они призваны лишь поставлять значение базовому классу (а не вычислять его), то этими методами можно вполне пользоваться _вместо_ полей базового класса.
S>Если требуются какие-то вычисления и кэширование полученных значений в полях базового класса — можно использовать отложенную инициализацию (с проверками при обращении к свойствам базового класса), например.
S>Можно вместо полей базового класса использовать абстрактные свойства, а производные классы будут хранить значения в _своих_ полях.
S>На худой конец, можно сделать в базовом классе защищенный метод Initialize(), возможно виртуальный, и наградить все финишные классы в цепочке наследования обязанностью вызвать его в своем конструкторе.
S>Все это я предпочту вызовам виртуальных методов из конструктора базового класса. Однако допускаю ряд случаев, когда никакие другие решения не подойдут по каким-либо причинам. В таком случае придется использовать это решение. Табу на него у меня нет
Всё это вопрос личных предпочтений и целесообразности, как мне кажется. Я совсем не утверждаю, что нужно дёргать виртуальные методы из конструктора в каждом втором классе. Мне это за всю жизнь понадобилось всего один раз, в других случаях удавалось сделать более красивое решение.
К сожалению, это было несколько лет назад, так что контекст я сейчас уже не вспомню.
В данном конкретном случае, с которого всё началось, я считаю наиболее подходящим решение rg45.
Здравствуйте, vmpire, Вы писали:
V>Всё это вопрос личных предпочтений и целесообразности, как мне кажется.
Согласен V>Я совсем не утверждаю, что нужно дёргать виртуальные методы из конструктора в каждом втором классе. Мне это за всю жизнь понадобилось всего один раз, в других случаях удавалось сделать более красивое решение. V>К сожалению, это было несколько лет назад, так что контекст я сейчас уже не вспомню.
Да не надо.
V>В данном конкретном случае, с которого всё началось, я считаю наиболее подходящим решение rg45.
Я тоже.