Подобными качествами обладают и другие функциональные языки, и выбор в пользу F# тут совсем не очевиден. Начиная с того, что он тащит с собой рантайм со сборщиком мусора и проверкой типов, который в функциональном языке особо-то и не нужен, заканчивая тем, что все библиотеки/фреймворки в .Net пишутся в первую очередь под C#.
Думаю, что основная причина отсутствия популярности у F# это, как ни странно, .Net, ведь любая программа на F# будет обладать примерно схожими эксплуатационными свойствами, как и такая-же программа на C#: она будет работать (плюс/минус) с той же скоростью, потреблять приблизительно столько же памяти и количество платформ на которых эти программы можно запустить будет точно таким же. Как результат, при выборе языка будут использованы такие критерии как:
Здорово, что они наконец задумались над внедрением нормальной кодо-генерации.
На самом деле, достичь сходных результатов (далеко не так изящно) можно было уже довольно давно с помощью того же Roslyn и T4.
Несколько лет назад, в одном WebApi2 проекте, я добавил генерацию класса, который был, по сути, билдером URL всех возможных маршрутов в этом API. Это довольно удобно, когда надо вставить в возвращаемые данные ссылки на другие ресурсы этого API. Можно, конечно, было записывать эти URL в виде строк, но это как-то не очень надежно, да и хотелось поэкспериментировать. В итоге, я добавил t4 файлик, который c помощью Roslyn делал перекомпиляцию некоторых файлов из проекта (например RouteConfig.cs) и прямо во время кодо-генерации, запустив перекомпилированный код, я получал экземпляр HttpConfiguration со всем Routes. После этого создать билдер было уже довольно просто.
Пример хороший, поскольку люди применяли этот подход и настойчиво рекомендовали другим, еще в то время когда он реально не работал, не смотря на вроде бы логичное обоснование. Это исправление рантайма, согласно тому же Рихтору (сам не проверял, может это уже устарело), так и не задокументировали, так что теоретически volatile read тут нужен
Смысл инкапсуляции это размещение в одном компоненте данных и методов, которые с ними работают. Скрытие реализации это скорее следствие.
внутренняя реализация от вас скрыта и не должна вас интересовать.
Если эта реализация идемпотентна, работает быстро, не потребляет лишней памяти и не кидает исключений, то да реализация такого свойства меня вряд ли заинтересует. Но все приведенные выше примеры использования явно этим критериям не удовлетворяют.
Опять же, основная идея которую я хочу донести, что вызывая метод я обычно допускаю побочные эффекты и могу, например, добавить try/catch. От свойств же, я побочных эффектов не ожидаю и вряд ли стану писать так:
try
{
var name = obj.Name
}
catch(Exception)
{
...
}
Чувствую пора подключать "авторитетные источники" )) У Рихтера в Главе 10 “Свойства” есть целый раздел “Осторожный подход к определению свойств (стр. 268)” в котором он объясняет почему ему эта возможность языка не нравится, и в целом, я разделяю его мнение.
Если бы я знал, что дело дойдет до ссылок "на авторитетные источники" я бы, конечно, комментарий написал бы более аккуратно. Но на самом деле я просто хотел показать, что люди (в том числе и программисты) часто следуют ритуалам не понимая причин их появления, и даже не пытаясь поставить под сомнение их разумность.
С конвенциями вообще есть очень показательный пример — раньше можно было встретить рекомендацию всегда копировать ивент в локальную переменную и только после этого проверять её на null:
public event EventHandler MyEvent;
protected OnMyEvent()
{
var myEvent = MyEvent;
if(myEvent != null){
myEvent(this, EventArgs.Empty);
}
}
Мол таким образом мы можем быть уверенны, что от свойства не отписались в другом потоке. Но тот факт, что компилятор просто убирал эту переменную в Release никого не смущал. Дошло дело до того, что Microsoft поменяла компилятор для распознавания этого шаблона.
Добавление логики в свойства это и есть breaking change который может привести к серьезным последствиям, ведь никто от ваших свойств не ожидал побочных эффектов и обращался к ним в цикле на миллион итераций из разных потоков.
Вообще ветка началась с фразы "Но если вы на 100% не знаете зачем вам нужны геттеры и сеттеры, то на мой взгляд, лучше их не использовать" в MVVM, я полагаю, вы знаете зачем вам геттеры и сеттеры.
По поводу примера с Name в 100500 местах, то если вы внезапно добавите костыль логику с кешироваем, логированием или еще чем-нибудь в этом духе, то есть шанс получить проблемы с производительностью, дедлоки и просто внезапные падения в продакшене.
Не совсем понял про "объект в инварианте" но если вы про ситуацию:
class Article {
private String name
public function getName(): String {
return this.name
}
public doSomeMagic(): void{
//Magic...
this.name = "magic value";
}
}
то тут наличие геттера, с точки зрения традиционного понимания ООП, становится обоснованным.
Мой изначальный комментарий был про:
public function getName(): String {
return this.name
}
public function setName(value: String): void {
this.name = value;
}
Ужаса -Ужаса в таких property обычно нет, как, впрочем, и пользы от них. Они, скорее, напоминают "народные приметы" такие как "постучать по дереву" или "сплюнуть через левое плечо". Необходимость в них может, внезапно, возникнуть при необходимости сериализции, с использованием не самых продвинутых сериализаторов (привет newtonsoft), что опять же, скорее всего, связанно с Linq Expressions, дающими очень удобную оболочку над ML Emit.
Есть одно ключевое слово которое очень хорошо контролирует состояние — readonly (или final). Если же возникает необходимость сделать некоторое стояние изменяемым, то опять же лучше это сделать с помощью метода который явно дает понять вызывающему, что этот вызов повлечет "побочный эффект".
Тут в .Net хабе пару дней назад появилась статья Неудачная статья про ускорение рефлексии, после которой я таки, в первый раз в своей жизни, нашел практическое применение сеттерам. Дело в том, что помощью сеттера мы можем присвоение значения выразить в виде выражения (где оно скрывается под вызовом функции), что позволяет нам создавать новый код во время выполнения используя более-менее простые Linq Expression вместо трудоемкого ML Emit. Но если вы на 100% не знаете зачем вам нужны геттеры и сеттеры, то на мой взгляд, лучше их не использовать, т.к. они, в лучшем случае, бесполезны.
Подобными качествами обладают и другие функциональные языки, и выбор в пользу F# тут совсем не очевиден. Начиная с того, что он тащит с собой рантайм со сборщиком мусора и проверкой типов, который в функциональном языке особо-то и не нужен, заканчивая тем, что все библиотеки/фреймворки в .Net пишутся в первую очередь под C#.
Думаю, что основная причина отсутствия популярности у F# это, как ни странно, .Net, ведь любая программа на F# будет обладать примерно схожими эксплуатационными свойствами, как и такая-же программа на C#: она будет работать (плюс/минус) с той же скоростью, потреблять приблизительно столько же памяти и количество платформ на которых эти программы можно запустить будет точно таким же. Как результат, при выборе языка будут использованы такие критерии как:
Здорово, что они наконец задумались над внедрением нормальной кодо-генерации.
На самом деле, достичь сходных результатов (далеко не так изящно) можно было уже довольно давно с помощью того же Roslyn и T4.
Несколько лет назад, в одном WebApi2 проекте, я добавил генерацию класса, который был, по сути, билдером URL всех возможных маршрутов в этом API. Это довольно удобно, когда надо вставить в возвращаемые данные ссылки на другие ресурсы этого API. Можно, конечно, было записывать эти URL в виде строк, но это как-то не очень надежно, да и хотелось поэкспериментировать. В итоге, я добавил t4 файлик, который c помощью Roslyn делал перекомпиляцию некоторых файлов из проекта (например RouteConfig.cs) и прямо во время кодо-генерации, запустив перекомпилированный код, я получал экземпляр HttpConfiguration со всем Routes. После этого создать билдер было уже довольно просто.
Зависит от того что вы вкладываете в понятие "консистентный". Как-минимум он должен быть консистентным настолько, что бы можно было вызвать метод Init
Например, будет ли объект типа SqlConnection консистентым перед вызовом Open()? (рассмотрим вариант, что connectionString указан)?
Есть множество доводов в пользу того, что бы не помещать сложную логику в конструктор.
Пример хороший, поскольку люди применяли этот подход и настойчиво рекомендовали другим, еще в то время когда он реально не работал, не смотря на вроде бы логичное обоснование. Это исправление рантайма, согласно тому же Рихтору (сам не проверял, может это уже устарело), так и не задокументировали, так что теоретически volatile read тут нужен
Вы не находите такой код странным?
А вот тут, вроде, становится понятно, что происходит:
а вот в этом случае, сеттеры вообще не смогут выполнить задуманное:
Смысл инкапсуляции это размещение в одном компоненте данных и методов, которые с ними работают. Скрытие реализации это скорее следствие.
Если эта реализация идемпотентна, работает быстро, не потребляет лишней памяти и не кидает исключений, то да реализация такого свойства меня вряд ли заинтересует. Но все приведенные выше примеры использования явно этим критериям не удовлетворяют.
Опять же, основная идея которую я хочу донести, что вызывая метод я обычно допускаю побочные эффекты и могу, например, добавить try/catch. От свойств же, я побочных эффектов не ожидаю и вряд ли стану писать так:
Чувствую пора подключать "авторитетные источники" )) У Рихтера в Главе 10 “Свойства” есть целый раздел “Осторожный подход к определению свойств (стр. 268)” в котором он объясняет почему ему эта возможность языка не нравится, и в целом, я разделяю его мнение.
Если бы я знал, что дело дойдет до ссылок "на авторитетные источники" я бы, конечно, комментарий написал бы более аккуратно. Но на самом деле я просто хотел показать, что люди (в том числе и программисты) часто следуют ритуалам не понимая причин их появления, и даже не пытаясь поставить под сомнение их разумность.
CLR via C# Джефри Рихтер 3-е русское издание Глава 11. События (стр. 277)
С конвенциями вообще есть очень показательный пример — раньше можно было встретить рекомендацию всегда копировать ивент в локальную переменную и только после этого проверять её на null:
Мол таким образом мы можем быть уверенны, что от свойства не отписались в другом потоке. Но тот факт, что компилятор просто убирал эту переменную в Release никого не смущал. Дошло дело до того, что Microsoft поменяла компилятор для распознавания этого шаблона.
Добавление логики в свойства это и есть breaking change который может привести к серьезным последствиям, ведь никто от ваших свойств не ожидал побочных эффектов и обращался к ним в цикле на миллион итераций из разных потоков.
Вообще ветка началась с фразы "Но если вы на 100% не знаете зачем вам нужны геттеры и сеттеры, то на мой взгляд, лучше их не использовать" в MVVM, я полагаю, вы знаете зачем вам геттеры и сеттеры.
По поводу примера с Name в 100500 местах, то если вы внезапно добавите
костыльлогику с кешироваем, логированием или еще чем-нибудь в этом духе, то есть шанс получить проблемы с производительностью, дедлоки и просто внезапные падения в продакшене.В такой ситуации пишите просто
Преимущество в том, что если вам вдруг надо будет добавить геттер-сеттер (чего обычно не происходит), то вы их добавите.
Саму же логику в пропертях разбирали уже много раз с момента выхода еще самого первого C#.
Не совсем понял про "объект в инварианте" но если вы про ситуацию:
то тут наличие геттера, с точки зрения традиционного понимания ООП, становится обоснованным.
Мой изначальный комментарий был про:
Ужаса -Ужаса в таких property обычно нет, как, впрочем, и пользы от них. Они, скорее, напоминают "народные приметы" такие как "постучать по дереву" или "сплюнуть через левое плечо". Необходимость в них может, внезапно, возникнуть при необходимости сериализции, с использованием не самых продвинутых сериализаторов (привет newtonsoft), что опять же, скорее всего, связанно с Linq Expressions, дающими очень удобную оболочку над ML Emit.
Есть одно ключевое слово которое очень хорошо контролирует состояние — readonly (или final). Если же возникает необходимость сделать некоторое стояние изменяемым, то опять же лучше это сделать с помощью метода который явно дает понять вызывающему, что этот вызов повлечет "побочный эффект".
Тут в .Net хабе пару дней назад появилась статья Неудачная статья про ускорение рефлексии, после которой я таки, в первый раз в своей жизни, нашел практическое применение сеттерам. Дело в том, что помощью сеттера мы можем присвоение значения выразить в виде выражения (где оно скрывается под вызовом функции), что позволяет нам создавать новый код во время выполнения используя более-менее простые Linq Expression вместо трудоемкого ML Emit. Но если вы на 100% не знаете зачем вам нужны геттеры и сеттеры, то на мой взгляд, лучше их не использовать, т.к. они, в лучшем случае, бесполезны.