Как стать автором
Обновить

Комментарии 55

Не нашел для себя ответа, где были бы полезны var-поля, будь они
Согласен. Никогда даже не задумывался о подобном, незачем как-то. Типы .NET — это контракты со строгой типизацией. Для тех мест, где нужно больше loose-овости и динамичности есть dynamic.
var никакого отнашения к динамической типизации не имеет.
Да ладно??? Серьезно? Ну-ка поподробнее!

Ну а если серьезно, я нигде и не говорил о том, что var имеет отношение к dynamic.
var — типы строятся компилятором в процессе компиляции (выводятся из правой части). Именно поэтому нельзя сделать, например:
var N;
N=3;

В статье как-то совсем не освещено, например, отличие var от dynamic.

И еще: я не увидел ответа на поставленный вопрос. Как мне всегда казалось: поля у классов имеют методы установки и получения значений (явные или неявные), в связи с чем при объявлении поля (а оно происходит без инициализации) нельзя определить верные сигнатуры методов.
> В статье как-то совсем не освещено, например, отличие var от dynamic.

Некоторые принципы проектирования, такие как Single Responsibility Principle применяются не только при создании программ, но и, например, при написании статей. У нормальной статьи есть определенная цель, которую преследует автор при ее написании. Цель этой статьи отражена в названии и нескольких первых абзацах.

И в целях автора просто не было целей сравнивать var и dynamic по нескольким причинам. Во-первых, это откровенный баян; в любой вводной статье про неявно типизированные переменные будет сказано «не путайте var с динамической типизацией». Во-вторых, у статьи есть определенное «предусловие»; предполагается, что автор знает, что такое неявно типизированные переменные и хочет узнать/понять, почему не бывает неявно типизированных полей; в-третьих, банально, var появился раньше, чем dynamic; из этого довольно легко сделать вывод, что это не одно и тоже.

А вот ваше последнее предположение совсем интересное. У полей нет никаких методов установки и получения значений. Вы путаете поля со свойствами. Так что последние несколько ваших фраз таки и остались для меня загадкой.
Да ну, я же не всерьез спрашивал :)

Конечно же я знаю в чем отличия. А зачем в статье писать про отличия var от dynamic? Разные вещи для разных задач, зачем их сравнивать.
Дык исходный вопрос был в том, почему их нет, вот я и объяснил, почему их нет и, скорее всего, не будет.
Спасибо, что объяснил. Мне этих var в поле не хватало, как раз.
class A
{
var _isInit = false;
}

Компилятор должен подставить bool (и все подсказки должны показывать bool)
Лично мне так читаемее (по аналогии с переменными).

Ниже есть пример, когда тип поля намного сложнее, и экономия по читаемости больше.
Имхо, как раз наоборот. Когда я пробегаю глазами список полей класса в чужом коде, я не смотрю на значения, а только по типы, ибо они в столбик выровнены. Соответственно, надо было бы куда-то лезть, думать, а этого делать не хочется.
Во-первых, выравнивание по значениям тоже возможно. А во-вторых, это ровно то же замечание, которое применимо к var-переменным — и Липперт по этому поводу писал, что семантика переменной обычно важнее ее типа, а когда тип имеет реальное значение var использовать не стоит.
Всё равно, когда часть типов указанна как var, другая — прописана явно — эта каша будет напрягать. А задать все типы неявно в сложном классе, скорее всего не выйдет.

Семантика семантикой, но программы люди пишут. Собственно код var flag = defaultFlagValue; вас таки заставит лезть смотреть определение этого defaultFlagValue. (const string defaultFlagValue = «undefined»; :) )

И это дополнительное поле для ошибок. Вообще, я стараюсь автоматической типизации именно по этой причине. Написать можно не то, что хотел, и компилятор ничего не скажет. Что-нибудь типа var i = 0.; может принести кучу веселья.
«var flag = defaultFlagValue»
Вот за это как раз и надо бить.

Во-первых, flag бывает только bool, тут не надо лезть и смотреть (а программиста, который вписал туда string — бить). Во-вторых, названий типа flag надо избегать (привет МакКоннелу), потому что совершенно не понятно, что этот flag отмечает.

И вары тут совершенно не при чем.
Я пользуюсь следующим правилом (оно у нас даже в Coding Standart-е прописано): не пользоваться var-ом тогда, когда поведение кода будет неочивдно ввиду неявной типизации.

В большинстве случаев тип либо очевиден, либо не важен, но есть моменты, когда без него никак; вот в этом случае используем явную типизацию, в остальных случаях спокойно пользуемся var-ами.
Ну это как раз соответствует тому, о чем пишет Липперт.
Проблема в том, что тот, кого надо быть — зачастую вне досягаемости :) Например в контролах, если не ошибаюсь, инфраджестика вместо boolean в свойствах использовался свой enum с тремя значениями, а поля назывались так же, как и в стандартных WinForms (isVisible, isHidden, ...) В общем МакКоннела читали далеко не все, и не все до этого понимания доходят каким-либо другим путём.

Так что, имхо, лучше не давать подкладывать грабли ещё на уровне синтаксиса языка.

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

«в готовых сборках значения, присвоенные в объявлении поля, не отображаются»
В готовых сборках поля, поскольку уже скомпилированы, показываются как реальный тип, а не как var.

(не будем тут говорить о валидности видимых за пределами класса — и тем более сборки — полей)
Согласен про названия.

Бесит, когда переменные называются что-то типа blnSomething, chkSmthOk или в этом духе, пусть даже перед ними bool стоит, все равно нихрена не понятно.

Я предпочитаю что-нибудь вроде:
var isSuccessful = Perform.SomeOperation(data);
Собственно, об этом и пишет Липперт: при адекватных названиях переменных знание ее типа в терминах компилятора обычно излишне.
Не понимаю, в какой ситуации типизированные var-ы вообще могут пригодиться.
Всмысле «типизированные var-ы»? Всмысле var-поля?
Да, в смысле var-поля.
class Foo
{
private readonly var list = new List();
}
Иногда действительно хочется написать так, когда не List а какой-нибудь сложный MultiDictionary объявлен. Но, к счастью, других применений var-on-field придумать не выходит.
НЛО прилетело и опубликовало эту надпись здесь
Ну, на самом деле, var постоянно использую для всех локальных переменных, где это возможно, очень удобно.

А вот насчет подчеркивания — кто бы что ни говорил, что, мол, это какие то «сиплюсплюсные» атавизмы — не согласен, мне удобно, когда все private-поля в стиле "_someField". Кстати, в вашем примере можно вообще обойтись без объявления поля. Или корпоративный стиль и это запрещает? :)
НЛО прилетело и опубликовало эту надпись здесь
'Что, мне правда надо было вложить немного логики в геттер/сеттер, чтобы вы не смогли придраться?"

Ага! Ну просто на самом деле глаз то цепляется :)
На всякий случай напомню, что в C++ имена с "_" в качестве префикса зарезервированы, так что не "_someField" а «someField_».

А теперь мой личный вопрос: а зачем как-то выделять (суффиксом "_", префиксом «m_») приватные и публичные поля? Внутри класса на мой взгляд нет разницы между приватными и публичными полями, и различать их незачем. Вне класса всё равно видны только публичные поля, так что отличать приватные поля от них на уровне именования нет необходимости.
Ну, просто набираешь в любом месте внутри класса "_" и IntelliSense сразу список полей показывает. Ну и плюс, то же самое с различением полей и свойств внутри класса. По коду сразу становится понятно, что работа идет с закрытым внутренним состоянием класса.
На самом деле у публичных и закрытых полей идиомы именования очень сильно отличаются. Если закрытые/защищенные рекомендуется называть lowerCase (с подчеркиванием или без него), то открытые поля — CamelCase. Связано это с тем, что семантика закрытых и открытых полей сильно отлисчается, а смысл в смешивании закрытых и открытых полей в одном классе для меня вообще не ясен.

Не, я понимаю, что есть простые data-классы, которые только и состоят из нескольких публичных полей. Но это довольно редкий кейс. Да и вообще, использовать открытые поля в языке с автосвойствами вообще кажется непонятным.
public dynamic someField = new {Name = «name», Value = 12};

Problem?
Поля анонимных типов read-only, если кто не в курсе.
Ага, проблем:)

Речь ведь шла не о динамической типизации, а о выводе типов полей. Это разные вещи.

dynamic очень здорово бъет по перформансу, ломает интелли-сенс и делает overload resolution для человека еще более сложной задачей.

Так что, да, таки проблем.
Ну он не так уж вот прям и бьет по нему. Разумеется, не так быстр, как статические типы, но вполне себе. Ну и использовать с умом нужно, конечно.

Интеллисенс — тут да, никуда не денешься.
Речь только о типах, известных на этапе компиляции.
Использую (почти) весь сахар последнего C#. Это и неявная типизация (тем более, для LINQ она практически необходима), и автоматические свойства, и оператор ??, и инициализация в конструкторе, и значения по умолчанию, и прочее-прочее.
Во-первых, нахожу это удобным. В общем случае текст становится более удобочитаемым. А во-вторых, банально меньше времени на кодинг уходит, даже невзирая на IntelliSense и R#.
Есть мнение, что большая часть того, что вы описали — не последний C#.
Ну пусть это будет «последних», так точнее. Я имел в виду улучшения после «классического» C# 2.0.
НЛО прилетело и опубликовало эту надпись здесь
Неявная типизация полезна только в коде методов, но никак не в полях. Разработчик всегда должен знать, с чем он работает, а если везде ставить var, то вообще что-то непонятное будет.

Я промолчу о том, что var не всегда помогает.

var doc = new XmlDocument();
doc.LoadXml("file.xml");

var xmlElement = doc.GetElementByTagName("someTag");

foreach(var item in xmlElement)
{
     //Do Something
}


В этом случае item будет определяться, как object, так что иногда явная типизация просто необходима.
var doc = new XmlDocument();
doc.LoadXml("file.xml");

var xmlElement = doc.GetElementByTagName("someTag");

foreach(XmlNode item in xmlElement)
{
     //Do Something
}
То что в данном случае var определяется как object проблема типа коллекции.
Правильно записать
foreach(var item in xmlElement.Cast<XmlNode>())
В этом случае происходит отделение мух от котлет и все действия выполняются раздельно:
— перечисление коллекции определяется методом foreach
— коллекция задаётся через xmlElement
— отдельно описано преобразование типа (если определить тип в foreach то не ясно подставлен ли тип или происходит неявный каст)
— отдельно задан item, к которому будут обращаться при перечислении коллекции.
Но в данном случае мне кажется, что foreach(XmlNode item in xmlElement) явно удобнее foreach(var item in xmlElement.Cast())
Сделаю акцент на этом вот пункте от юзера steck:
"- отдельно описано преобразование типа (если определить тип в foreach то не ясно подставлен ли тип или происходит неявный каст)".

То есть, мы явно указываем преобразование, а не тип, что в данном случае правильно — ибо каст имеет место.
т.е. мой пример является неправильным?
Эстетически пример красивее, но по логике — более опасен.

В случае с объявление типа внутри перечисления происходит сразу две вещи.
Одна обязательная — объявление типа элемента.
Вторая необязательная — объявление даункаста.
Поскольку даункаст операция не безопасная, то мне хотелось бы её контролировать отдельно.

В случае с объявление типа мы можем сделать и апкаст, например
foreach(object i in new List<int>{ 1, 2, 3,})
но после введение ко- и контравариантности в дженерики я даже не знаю где такое преобразование может понадобиться.
Я этого не говорил. Но более очевидной записью будет каст коллекции.
Вполне возможно, хотя плохо выглядит конструкция из-за того что каст удлиняет строчку.

В последнее время мне очень редко приходится писать подобные конструкции. Обычно что-то вроде:
xmlElement.Cast<XmlNode>().Where(SomeCondition).SelectMany(SomeFunction).ForEach(Process)
Это избавляет меня от изменяемых состояний(item в примере как раз меняется по прохождению через коллекцию и если, скажем, использовать его в замыкании то на этом можно попасть), даёт возможность более декларативно записывать код.
Этот пример я обязательно запомню!
«Изменяемые состояния» — это вы про Modified Closure?

«даёт возможность более декларативно записывать код»
Более функционально, это функциональный стиль.
> «Изменяемые состояния»
Моя русский плохо плохо. =)
Я имел ввиду, что item в foreach это переменная. В то время как в лямбде скорее всего параметр меняться не будет.

Ну а ворнинг решарпера как раз предупреждает о подобной проблеме:
var list = new List<Action>();
foreach (var i in Enumerable.Range(1, 5))
{
    list.Add(() => Console.WriteLine(i));
}
list.ForEach(x => x());


> Более функционально, это функциональный стиль.
Более функциональный стиль в данном случае лишь следствие. Я определил как должен вести себя поток управления. Декларативно описал. Не описывая прямого поведения кода.
Это декларативный стиль.

Функциональный стиль — это когда функции являются первоклассными значениями и используются функции высших порядков. В данном случае цепочкой вызовов Linq методов декларативно описивает поток исполнения, пускай и используя ФВП.
Так что функциональный стиль тут вторичен.

Это тоже функциональненько, но без декларативности
foreach(var a in collection.Cast<A>())
{
    Func<A,B> builder = Generate(genericBuilder, () => a);
    foreach(var b in builder(a))
    { //bla bla bla
    }
}
Ну кому как удобнее. Cast(), например, позволяет сразу вызвать Where, где можно прописать фильтр на уже типизированные XmlNode.
Ошибся веткой, это ответ сюда.
Мне, например, удобнее просто LINQ 2 Xml использовать. Там и API проще, и тестировать его легче, да и с LINQ-ом он дружнее.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории