Здравствуйте, nt2000, Вы писали:
N>Как определяете при объявлении переменных? Есть признаки или надо просто запомнить?
А можно примеры? Так-то, тут такое разнообразие..
Ну вот ты и ответил на свой вопрос. Константы без new, остальное с new. Список типов констант есть в описании языка, он совсем небольшой. И есть еще одна форма array initializer без new.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Здравствуйте, nt2000, Вы писали:
N>>
int a=1;
N>>a=2;
N>>a=3;
НС>Ну вот ты и ответил на свой вопрос. Константы без 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[] - имя класса массива интов
Здравствуйте, 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); // аналогично
Здравствуйте, 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?
Здравствуйте, 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 или ссылку на другой объект, то наш первый объект, если на него больше никто не ссылается, будет сожран сборщиком мусора, если сборщик мусора этого захочет.
Здравствуйте, varenikAA, Вы писали:
AA>Чисто визуально даже, приятней было бы: AA>
AA>let d = DateTime()
AA>
Возможно, я просто привык, но мне нравится, когда вызов конструктора типа визуально отличается от вызова какого-то метода. Не вижу тут каких-то особых неудобств и вопросом, нужно в конкретном месте писать new или не нужно, никогда не задавался.
Здравствуйте, 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, Вы писали:
A>>Передать в Proc1 можно не что угодно, а только то, что Proc1/WriteLine смогут переварить без ошибок компиляции или ошибок в рантайме. Что конкретно они могут переваривать, из объявления Proc1 не видно. AA>Это просто, если вывод типа не справился или если передается невалидный аргумент, то компилятор выдаст подробную ошибку. В первом случае можно будет явно указать ожидаемый тип (уточнение). Но это требуется очень редко.
Игра в угадайку получается. Хорошо, когда ты функцию написал сам недавно и знаешь, что она хочет и что может. А когда берёшь чужой API, то сидишь и смотришь на него, как баран на ворота, потому что кто-то поленился типы прописать. Впрочем, питоновский синтаксис даже с типами не фонтан.
Здравствуйте, alexzzzz, Вы писали:
A>Здравствуйте, varenikAA, Вы писали:
A>>>Передать в Proc1 можно не что угодно, а только то, что Proc1/WriteLine смогут переварить без ошибок компиляции или ошибок в рантайме. Что конкретно они могут переваривать, из объявления Proc1 не видно. AA>>Это просто, если вывод типа не справился или если передается невалидный аргумент, то компилятор выдаст подробную ошибку. В первом случае можно будет явно указать ожидаемый тип (уточнение). Но это требуется очень редко.
A>Игра в угадайку получается. Хорошо, когда ты функцию написал сам недавно и знаешь, что она хочет и что может. А когда берёшь чужой API, то сидишь и смотришь на него, как баран на ворота, потому что кто-то поленился типы прописать. Впрочем, питоновский синтаксис даже с типами не фонтан.
Если использовать ide, то типы в подсказках будут.
Если нет — подскажет компилятор(если это хороший компилятор). Вы же не пишите программу на листочке бумаге?
Ну и не знаю, вы на чем кодите? В C# обобщенное программирование и утиная типизация повсюду.
Вспоминаю старый слоган: "настоящий хакер кодит в notepad".
Здравствуйте, 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 компилятор уже отработал, и соображать некому. Статический код проходит верификацию при загрузке; рантайму запрещено поднимать в память код, который может вызвать ошибку типизации.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.