Здравствуйте, nt2000, Вы писали:
N>Как определяете при объявлении переменных? Есть признаки или надо просто запомнить?
Объявление переменной и new — вещи перпендикулярные. Чтобы объявить переменную, достаточно написать её тип, её имя и всё — переменная объявлена:
int x;
DateTime d;
MyClass instance;
Дальше мы, вероятно, хотим этой переменной что-нибудь присвоить. Вопрос, что именно? Если какое-то уже существующее значение (константу, литерал, результат выражения, значение другой переменной...), то берём и присваиваем без всяких new:
x = 5;
x = y + 10;
dateTime = default;
dateTime = DateTime.Now;
instance = null;
instance = someOtherInstance;
Оператор new требуется, когда нужно создать новый объект ссылочного типа (по-другому новые объекты ссылочных типов не создаются, экзотику не рассматриваю):
MyClass instance1 = new MyClass();
MyClass instance2 = new MyClass(...);
или нужно получить новое значение value-типа, через конструктор этого типа:
DateTime dateTime = new DateTime(...);
Для value-типов альтернативой вызову конструктора может быть ручная инициализация всех имеющихся полей. Например:
struct Vector3
{
public float x, y, z;
public Vector3(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
public Vector3(float value)
{
this.x = value;
this.y = value;
this.z = value;
}
}
Можно так:
Vector3 v1 = new Vector3(1, 2, 3); // объявили переменную и вызвали конструктор типа для "конструирования" нового значения этого типа
Vector3 v2 = new Vector3(1); // аналогично
Здравствуйте, nt2000, Вы писали:
A>>или нужно получить новое значение value-типа, через конструктор этого типа: A>>
DateTime dateTime = new DateTime(...);
N>Интересно чисто для самообразования.. N>Если переменные ради значений порождают огромные махины — классы, потом после получения желанных значений куда деваются эти классы? болтаются как бедные родственники никому не нужные? а если переменная захочет получить другие значения, то надо рожать опять новый экземпляр?
Сначала немножко про терминологию.
Есть ссылочные типы: классы, интерфейсы, делегаты, строки, массивы. Переменная ссылочного типа хранит ссылку (грубо говоря, указатель) на объект (по-другому экземпляр) этого типа, расположенный в куче (обычно). А объект/экземпляр в свою очередь хранит в себе полезные и некоторые служебные данные. Т.е. слово класс обозначает какой-то ссылочный тип, а объект или экземпляр класса ― это та штука, в которой хранятся данные этого типа.
Есть value-типы: структуры, перечисления (enum) и все встроенные числовые и логические типы (int, float, bool...). Переменная value-типа хранит непосредственно данные.
Есть ещё отдельно указатели, но хрен с ними.
Согласно спецификации, значением переменной ссылочного типа является ссылка на объект (который где-то там лежит и что-то хранит) или null, значением переменной value-типа являются непосредственно сами данные этого типа.
--
DateTime dateTime = new DateTime(...);
Оператор new нестрашный. В случае value-типов, а DateTime — это структура, value-тип, new просто обозначает вызов конструктора. Никакого объекта где-то в куче не создаётся, просто под переменную выделяется место на стеке (если переменная локальная) и вызывается конструктор, который заполняет это место правильными данными. Если переменная сильно временная и помещается в регистр процессора, то и место в стеке может не выделяться — это на усмотрение jit-компилятора.
никакой разницы не будет. Разве что конкретный JIT-компилятор в конкретных условиях может заинлайнить вызов функции, а может не заинлайнить. Разницы по потреблению памяти точно не будет.
Если тип ссылочный, например строка:
var s = new string('X', 3);
оператор new создаёт в куче экземпляр данного типа и вызывает его конструктор с заданными аргументами, который заполняет экземпляр типа нужными данными. На выходе получаем ссылку на этот экземпляр, которая потом сохраняется в переменную s.
Да, мы создали в куче новый объект, то это никакой не лишний промежуточный объект, а тот самый объект, ссылка на который в итоге будет храниться в переменной s.
Если переменной s позже изменить значение ― присвоить null или ссылку на другой объект, то наш первый объект, если на него больше никто не ссылается, будет сожран сборщиком мусора, если сборщик мусора этого захочет.
Ну вот ты и ответил на свой вопрос. Константы без new, остальное с new. Список типов констант есть в описании языка, он совсем небольшой. И есть еще одна форма array initializer без new.
Здравствуйте, nt2000, Вы писали:
N>Как определяете при объявлении переменных? Есть признаки или надо просто запомнить?
Когда имя класса структуры, тогда new.
var a = 1; //Численный литерал, не имя классаvar b = "str"; //строковый литерал, не имя классаvar c = f(a); //выражение, не имя классаvar d = new DateTime(...); // Имя структуры, нужно newvar e = DateTime.Now; // Выражение, не имя классаvar e = new int[10]; // специальный случай для массивов, но целом то же самое, ибо int[] - имя класса массива интов
Здравствуйте, varenikAA, Вы писали:
AA>Чисто визуально даже, приятней было бы: AA>
AA>let d = DateTime()
AA>
Возможно, я просто привык, но мне нравится, когда вызов конструктора типа визуально отличается от вызова какого-то метода. Не вижу тут каких-то особых неудобств и вопросом, нужно в конкретном месте писать new или не нужно, никогда не задавался.
Здравствуйте, varenikAA, Вы писали:
A>>Передать в Proc1 можно не что угодно, а только то, что Proc1/WriteLine смогут переварить без ошибок компиляции или ошибок в рантайме. Что конкретно они могут переваривать, из объявления Proc1 не видно. AA>Это просто, если вывод типа не справился или если передается невалидный аргумент, то компилятор выдаст подробную ошибку. В первом случае можно будет явно указать ожидаемый тип (уточнение). Но это требуется очень редко.
Игра в угадайку получается. Хорошо, когда ты функцию написал сам недавно и знаешь, что она хочет и что может. А когда берёшь чужой API, то сидишь и смотришь на него, как баран на ворота, потому что кто-то поленился типы прописать. Впрочем, питоновский синтаксис даже с типами не фонтан.
Здравствуйте, alexzzzz, Вы писали:
A>Здравствуйте, varenikAA, Вы писали:
A>>>Передать в Proc1 можно не что угодно, а только то, что Proc1/WriteLine смогут переварить без ошибок компиляции или ошибок в рантайме. Что конкретно они могут переваривать, из объявления Proc1 не видно. AA>>Это просто, если вывод типа не справился или если передается невалидный аргумент, то компилятор выдаст подробную ошибку. В первом случае можно будет явно указать ожидаемый тип (уточнение). Но это требуется очень редко.
A>Игра в угадайку получается. Хорошо, когда ты функцию написал сам недавно и знаешь, что она хочет и что может. А когда берёшь чужой API, то сидишь и смотришь на него, как баран на ворота, потому что кто-то поленился типы прописать. Впрочем, питоновский синтаксис даже с типами не фонтан.
Если использовать ide, то типы в подсказках будут.
Если нет — подскажет компилятор(если это хороший компилятор). Вы же не пишите программу на листочке бумаге?
Ну и не знаю, вы на чем кодите? В C# обобщенное программирование и утиная типизация повсюду.
Вспоминаю старый слоган: "настоящий хакер кодит в notepad".
Здравствуйте, nt2000, Вы писали:
N>Как определяете при объявлении переменных? Есть признаки или надо просто запомнить?
А можно примеры? Так-то, тут такое разнообразие..
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Здравствуйте, nt2000, Вы писали:
N>>
int a=1;
N>>a=2;
N>>a=3;
НС>Ну вот ты и ответил на свой вопрос. Константы без new, остальное с new. Список типов констант есть в описании языка, он совсем небольшой. И есть еще одна форма array initializer без new.
Здравствуйте, alexzzzz, Вы писали:
A>или нужно получить новое значение value-типа, через конструктор этого типа: A>
DateTime dateTime = new DateTime(...);
Интересно чисто для самообразования..
Если переменные ради значений порождают огромные махины — классы, потом после получения желанных значений куда деваются эти классы? болтаются как бедные родственники никому не нужные? а если переменная захочет получить другие значения, то надо рожать опять новый экземпляр?
Здравствуйте, nt2000, Вы писали:
N>Здравствуйте, alexzzzz, Вы писали:
A>>или нужно получить новое значение value-типа, через конструктор этого типа: A>>
DateTime dateTime = new DateTime(...);
N>Интересно чисто для самообразования.. N>Если переменные ради значений порождают огромные махины — классы, потом после получения желанных значений куда деваются эти классы? болтаются как бедные родственники никому не нужные? а если переменная захочет получить другие значения, то надо рожать опять новый экземпляр?
Так, для ясности из какого языка вы на C# перешли?
Здравствуйте, Danchik, Вы писали:
D>Здравствуйте, nt2000, Вы писали:
N>>Здравствуйте, alexzzzz, Вы писали:
A>>>или нужно получить новое значение value-типа, через конструктор этого типа: A>>>
DateTime dateTime = new DateTime(...);
И все-таки, интересно, почему в некоторых языках используется new?
Ведь он усложняет анализ кода.
На память в делфи Class.Create(), в nim — newPerson(), в немерле Class().
C, C#, и, внезапно, F# — new!
т.е. чтобы пользоваться в F# функциональщиной, нужно делать фабричную функцию:
let date () = new DateTime()
let d = date()
Чисто визуально даже, приятней было бы:
let d = DateTime()
В ди например, это имеет особый смысл, там выражение new(uint size,...)
определяет где будет создан объект(по умолчанию в куче), если же new для локальной переменной функции класса, то на стэке.
можно явно указать new(размер выделяемой памяти), естественно, можно расширять поведение инициализации, явно реализуя метод класса вида: new(uint size,int a, string b).
Но в C# по-моему, нужно было разрешить вызывать конструкторы как статические методы. Это логичнее.
В С++ new использует не для создания объекта, а для выделения динамической памяти. А так как сборщиков мусора там нема, то и отдельные ключ слова для выделения/освобождения памяти там очень кстати были.
А во всякие шарпы потащили new — чтоб было похоже на плюсы.
Здравствуйте, varenikAA, Вы писали:
AA>И все-таки, интересно, почему в некоторых языках используется new? AA>Ведь он усложняет анализ кода.
Я считю наоборот, упрощает. Хотя в это дело привычки, если человек начал с питона, то неудобно.
AA>На память в делфи Class.Create(), в nim — newPerson(), в немерле Class(). AA>C, C#, и, внезапно, F# — new!
Чем newPerson лучше new Person?
AA>Но в C# по-моему, нужно было разрешить вызывать конструкторы как статические методы. Это логичнее.
А почему не различать создание объекта здесь и сейчас и делегацию создания -- builder, fabric?
Здравствуйте, Jack128, Вы писали:
J>Здравствуйте, varenikAA, Вы писали:
J>В С++ new использует не для создания объекта, а для выделения динамической памяти. А так как сборщиков мусора там нема, то и отдельные ключ слова для выделения/освобождения памяти там очень кстати были. J>А во всякие шарпы потащили new — чтоб было похоже на плюсы.
Да, создание любого объекта начинается с выделения памяти(не только в си), но так как в управляемом коде все это делает виртуальная машина,
конечно, было бы логично использовать new только в unsafe-коде.
Здравствуйте, Sharov, Вы писали:
S>Здравствуйте, varenikAA, Вы писали:
S>Чем newPerson лучше new Person?
newPerson — функция. new Person — по смыслу тоже самое, но синтаксически отличается. Даже затрудняюсь сказать, new это оператор, выражение или инструкция...
AA>>Но в C# по-моему, нужно было разрешить вызывать конструкторы как статические методы. Это логичнее.
S>А почему не различать создание объекта здесь и сейчас и делегацию создания -- builder, fabric?
В управляемом коде это совершенно излишне. Даже с++ рекомендуют использовать библиотеки по управлению памятью(временем жизни объектов).
Здравствуйте, alexzzzz, Вы писали:
A>Здравствуйте, varenikAA, Вы писали:
AA>>Чисто визуально даже, приятней было бы: AA>>
AA>>let d = DateTime()
AA>>
A>Возможно, я просто привык, но мне нравится, когда вызов конструктора типа визуально отличается от вызова какого-то метода. Не вижу тут каких-то особых неудобств и вопросом, нужно в конкретном месте писать new или не нужно, никогда не задавался.
Вот, и у меня в голове такая же формула сидела. Сунулся проверить — хвать, а нет у Инта конструкторов с параметрами (.Net 4.0, старее не установлены).
Так что:
Здравствуйте, varenikAA, Вы писали:
AA>- близко, но не так хорошо как: AA> def Proc1(a) { AA> WriteLine(a()); AA> }; AA> Proc1(DateTime); AA> _ = ReadLine();
Не люблю питоноподобный синтаксис. Функция неизвестно что принимает и неизвестно что возвращает, если вообще возвращает. Вместо полезных типов имеем ключевое слова def, которое по сути бесполезно.
Вызов выглядит так, будто мы передаём в функцию Proc1 переменную или константу DateTime.
Здравствуйте, alexzzzz, Вы писали:
A>Здравствуйте, varenikAA, Вы писали:
AA>>- близко, но не так хорошо как: AA>> def Proc1(a) { AA>> WriteLine(a()); AA>> }; AA>> Proc1(DateTime); AA>> _ = ReadLine();
A>Не люблю питоноподобный синтаксис. Функция неизвестно что принимает и неизвестно что возвращает, если вообще возвращает. Вместо полезных типов имеем ключевое слова def, которое по сути бесполезно.
A>Вызов выглядит так, будто мы передаём в функцию Proc1 переменную или константу DateTime.
В том-то и дело, что передать можно все что угодно: от выражения до константы т.к. функции в ФП обощенные по умолчанию и специализируются
в момент использования.
Здравствуйте, varenikAA, Вы писали:
AA>>>- близко, но не так хорошо как: AA>>> def Proc1(a) { AA>>> WriteLine(a()); AA>>> }; AA>>> Proc1(DateTime); AA>>> _ = ReadLine();
A>>Не люблю питоноподобный синтаксис. Функция неизвестно что принимает и неизвестно что возвращает, если вообще возвращает. Вместо полезных типов имеем ключевое слова def, которое по сути бесполезно.
A>>Вызов выглядит так, будто мы передаём в функцию Proc1 переменную или константу DateTime. AA>В том-то и дело, что передать можно все что угодно: от выражения до константы т.к. функции в ФП обощенные по умолчанию и специализируются AA>в момент использования.
Передать в Proc1 можно не что угодно, а только то, что Proc1/WriteLine смогут переварить без ошибок компиляции или ошибок в рантайме. Что конкретно они могут переваривать, из объявления Proc1 не видно.
Здравствуйте, alexzzzz, Вы писали:
A>Здравствуйте, varenikAA, Вы писали:
AA>>>>- близко, но не так хорошо как: AA>>>> def Proc1(a) { AA>>>> WriteLine(a()); AA>>>> }; AA>>>> Proc1(DateTime); AA>>>> _ = ReadLine();
A>>>Не люблю питоноподобный синтаксис. Функция неизвестно что принимает и неизвестно что возвращает, если вообще возвращает. Вместо полезных типов имеем ключевое слова def, которое по сути бесполезно.
A>>>Вызов выглядит так, будто мы передаём в функцию Proc1 переменную или константу DateTime. AA>>В том-то и дело, что передать можно все что угодно: от выражения до константы т.к. функции в ФП обощенные по умолчанию и специализируются AA>>в момент использования.
A>Передать в Proc1 можно не что угодно, а только то, что Proc1/WriteLine смогут переварить без ошибок компиляции или ошибок в рантайме. Что конкретно они могут переваривать, из объявления Proc1 не видно.
Это просто, если вывод типа не справился или если передается невалидный аргумент, то компилятор выдаст подробную ошибку. В первом случае можно будет явно указать ожидаемый тип (уточнение). Но это требуется очень редко.
Здравствуйте, varenikAA, Вы писали:
AA>Если использовать ide, то типы в подсказках будут. AA>Если нет — подскажет компилятор(если это хороший компилятор). Вы же не пишите программу на листочке бумаге? AA>Ну и не знаю, вы на чем кодите? В C# обобщенное программирование и утиная типизация повсюду.
В C# если метод принимает T, значит он принимает любые T, чем бы они ни были. Если на T есть ограничения, то они явно заданы в виде type constraints.
int Foo<T>(T item) where T : struct, IComparable<T>
Метод принимает только те T, которые value-типы и реализуют интерфейс IComparable<T>. С такими T он работать точно умеет. Что-то другое в него хрен запихнёшь.
Здравствуйте, alexzzzz, Вы писали:
A>Здравствуйте, varenikAA, Вы писали:
AA>>Если использовать ide, то типы в подсказках будут. AA>>Если нет — подскажет компилятор(если это хороший компилятор). Вы же не пишите программу на листочке бумаге? AA>>Ну и не знаю, вы на чем кодите? В C# обобщенное программирование и утиная типизация повсюду.
A>В C# если метод принимает T, значит он принимает любые T, чем бы они ни были. Если на T есть ограничения, то они явно заданы в виде type constraints.
A>
int Foo<T>(T item) where T : struct, IComparable<T>
A>Метод принимает только те T, которые value-типы и реализуют интерфейс IComparable<T>. С такими T он работать точно умеет. Что-то другое в него хрен запихнёшь.
И что? Вы описали интерфейс. Языки с выводом типов автоматически сгенерят нужную версию функции, в отличии от C# 2-й версии, где надо было писать int x = Foo<Person>(null);
В последних можно так: var x = Foo((Person)null);
Вы до сих пор указываете параметр обобщенных методов?
int Foo<T>(T item) where T : struct, IComparable<T>
A>>Метод принимает только те T, которые value-типы и реализуют интерфейс IComparable<T>. С такими T он работать точно умеет. Что-то другое в него хрен запихнёшь. AA>И что? Вы описали интерфейс.
Этот метод не принимает экземпляры классов, пусть даже реализующих интерфейс. Он принимает только числа и структуры, реализующие интерфейс. Это явно указано в объявлении. Для каждого подходящего типа будет сгенерирована своя версия метода.
AA>в отличии от C# 2-й версии, где надо было писать int x = Foo<Person>(null); AA>В последних можно так: var x = Foo((Person)null);
Не понял. В режиме C# 2.0 компилятор принимает оба варианта. Они равнозначны по смыслу и результату. У литералов null и default нет типа. Если вывести его неоткуда, надо указать вручную. Иначе какая из перегрузок WriteLine должна быть вызвана и что должен вернуть подобный метод, если передать в него null без типа:
T Recreate<T>(T x) where T : new() // любые типы с конструктором без параметров
{
Console.WriteLine(x);
return new T();
}
Здравствуйте, alexzzzz, Вы писали:
A>Не понял. В режиме C# 2.0 компилятор принимает оба варианта. Они равнозначны по смыслу и результату. У литералов null и default ]?
Я не совсем об этом.
Вот например, вы пишете метод:
bool Test<T>(T a, T b)
{
return a>b;
}
WriteLine(Test(1,2));
Что получится?
Или в d:
bool Test(T)(T a, T b)
{
return a>b;
}
writeln(Test(1,2));
Здравствуйте, varenikAA, Вы писали:
A>>Не понял. В режиме C# 2.0 компилятор принимает оба варианта. Они равнозначны по смыслу и результату. У литералов null и default ]? AA>Я не совсем об этом. AA>Вот например, вы пишете метод: AA>
AA>bool Test<T>(T a, T b)
AA>{
AA> return a>b;
AA>}
AA>Что получится?
Ничего не получится. C# не даст написать код, принимающий любые T, который не валиден для любых T.
Здравствуйте, alexzzzz, Вы писали:
A>Здравствуйте, varenikAA, Вы писали:
A>>>Не понял. В режиме C# 2.0 компилятор принимает оба варианта. Они равнозначны по смыслу и результату. У литералов null и default ]? AA>>Я не совсем об этом. AA>>Вот например, вы пишете метод: AA>>
AA>>bool Test<T>(T a, T b)
AA>>{
AA>> return a>b;
AA>>}
AA>>Что получится?
A>Ничего не получится. C# не даст написать код, принимающий любые T, который не валиден для любых T.
Здравствуйте, varenikAA, Вы писали: A>>Ничего не получится. C# не даст написать код, принимающий любые T, который не валиден для любых T.
AA>вот я и спрашиваю:
var x = Test(1,2);
— это валидное выражение?
Это выражение валидно, но до его компиляции дело не дойдёт.
Компиляция упадёт ещё на bool Test<T>(T a, T b) => a>b;, т.к. компилятору неизвестно, есть ли для T operator>().
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, varenikAA, Вы писали: A>>>Ничего не получится. C# не даст написать код, принимающий любые T, который не валиден для любых T.
AA>>вот я и спрашиваю:
var x = Test(1,2);
— это валидное выражение? S>Это выражение валидно, но до его компиляции дело не дойдёт. S>Компиляция упадёт ещё на bool Test<T>(T a, T b) => a>b;, т.к. компилятору неизвестно, есть ли для T operator>().
Согласен, но в случае если в контексте вызова только int комплятор вполне мог бы сообразить.
Забавно, что и Nemerle не справился с выводом типов, если подряд вызвать сначала с числами, а затем со строками.
Еще забавней выглядит Ди:
bool Test(T)(T a, T b)
{
return a > b;
}
void main()
{
import std.stdio ;
auto r1 = Test(10,20);
auto r2 = Test("10","20");
writeln(r1, r2);
}
Ну, а хитрее всех F#
let f a b = a > b //=> val f : a:'a -> b:'a -> bool when 'a : comparison
Здравствуйте, varenikAA, Вы писали:
S>>Компиляция упадёт ещё на bool Test<T>(T a, T b) => a>b;, т.к. компилятору неизвестно, есть ли для T operator>(). AA>Согласен, но в случае если в контексте вызова только int комплятор вполне мог бы сообразить.
Во время компиляции Test<T> у компилятора нет этого контекста. Он не знает, с какими аргументами Test будет вызываться. Объявление Test<T> может находиться в одной сборке, а десяток его вызовов с десятком разных типов аргументов ― в десятке других сборок (возможно, даже пока не существующих).
Здравствуйте, varenikAA, Вы писали: AA>Согласен, но в случае если в контексте вызова только int комплятор вполне мог бы сообразить.
Не мог бы. Код Test и код Main могут быть в разных сборках. К моменту вызова в main компилятор уже отработал, и соображать некому. Статический код проходит верификацию при загрузке; рантайму запрещено поднимать в память код, который может вызвать ошибку типизации.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.