Зачем переменным нужен тип?

10

Итак, мы пишем:

Customer c = new Customer();

Почему дизайн не такой, что мы пишем:

c = new Customer();
c.CreditLimit = 1000;

Компилятор может определить c пунктов для Клиента и позволить членам Клиента вызываться на c?

Я знаю, что мы можем написать:

IPerson c = new Customer();
IPerson e = new Employee();

чтобы можно было написать:

public string GetName(IPerson object)
{
    return object.Name
}

string name = GetName(c); // or GetName(e);

Но если бы мы написали:

c = new Customer();
e = new Employee();

мы могли бы еще написать:

public string GetName(object)
{
    return object.Name
}    

string name = GetName(c); // or GetName(e);

Компилятор может жаловаться на приведенный выше код, если тип ссылки на объект c не поддерживает свойство Name (так как он может проверять, какие члены используются в аргументе / параметре в методе), или среда выполнения может жаловаться.

Даже с динамическим ключевым словом C # мы все еще используем переменную type (определяется во время выполнения). Но зачем переменной вообще нужен тип? Я уверен, что должна быть веская причина, но я не могу думать об этом!

sturdytree
источник
12
Вот почему существуют динамические языки, такие как Python и Ruby (и другие). Там нет ответа на этот вопрос. Это факт, что некоторые языки используют объявления типов, а некоторые - нет.
S.Lott
2
"Переменные в этих языках вообще не имеют типа?" Правильный. переменные не имеют типа в Python. Объекты имеют тип, а переменные - это просто ссылки на объекты. Ваш вопрос на самом деле просто наблюдение. В частности, «не все языки требуют объявления переменных». Там нет ответа. Так что, скорее всего, это закроется как неконструктивное.
S.Lott
1
Возможно, вы захотите взглянуть на Boo , который позволяет объявлять переменные без каких-либо объявлений типов, но использует вывод типов для определения типов, поэтому вы не жертвуете преимуществами правильности и производительности строгой типизации.
Мейсон Уилер
2
var x = 1; - Вы имели в виду 32-битное, 64-битное, 16-битное число? байт? плавать? удваивать? десятичный? Это касается только примитивов.
Работа
4
Вы можете использовать varв C #, и всегда хорошо знать, где вы хотите объявить переменную.
Даниэль Литтл

Ответы:

22

Но зачем переменной вообще нужен тип?

  1. Это может выявить ошибки, когда недопустимое, неправильно набранное выражение назначено переменной. Некоторые языки имеют динамическую типизацию , которая жертвует гарантиями правильности типа для переменной для той гибкости, которую вы, похоже, желаете.
  2. Типы могут позволить компилятору генерировать более эффективный код. Динамическая типизация означает, что проверки типов должны выполняться во время выполнения.
Фред Фу
источник
1
Пожалуйста, объясните отрицательный голос.
Фред Фу
4
Кроме того, динамическая типизация приводит к снижению производительности, поскольку информацию о типе необходимо проверять во время выполнения.
Чарльз Сальвия
@CharlesSalvia: хорошая мысль, добавил это к ответу.
Фред Фу
2
@sturdytree: что касается «если у него нет типа, то он не может быть неправильно напечатан» - что касается правил языка, это правда. Но переменной все равно может быть присвоен неправильный тип с семантической точки зрения, например, вы неправильно набрали имя конструктора, и программа все еще работает, но не выполняет то, что вы хотите.
Фред Фу
5
@sturdytree Хотя это правда , что нет языков , которые имеют действительно безтипных переменные с проверкой типов, существуют языки , которые имеют определение типа . Другими словами, язык проверяет использование вами переменной и выводит тип из вашего использования. Если возникает конфликт (например, вы выполняете, a = new Bar()а затем вызываете метод из класса Baz), компилятор вызывает ошибку. Такие языки, как Haskell и OCaml, впервые применили вывод типов, но он присутствует в C # с varключевым словом.
квитанция
9

У вас есть абсолютно правильная точка зрения, языки, которые не отслеживают тип переменной, существуют и называются «динамически типизированными». В эту категорию входят такие языки, как JavaScript, Perl, Lisp и Python.

Преимущество, которое мы получаем от статически типизированного языка, заключается в дополнительной проверке ошибок во время компиляции.

Предположим, например, что у вас есть следующий метод:

public addCustomerContact(Customer client, Employee contact) {
   ...
} 

Было бы возможно, если у вас есть клиент bobи сотрудник jamesв вашем коде, чтобы по ошибке позвонить addCustomerContact(james, bob), что является недействительным. Но если компилятор не знает типы переменных, он не может предупредить вас о том, что вы сделали неверный вызов, вместо этого возникает ошибка во время выполнения ... и поскольку языки с динамической типизацией не проверяют Тип параметров, передаваемых в методы, эта проблема возникает всякий раз, когда ваш код пытается использовать свойства jamesобъекта только для клиента или свойства объекта только для сотрудника bob. Это может занять много времени после того, как пара (james, bob) была добавлена ​​в список контактов с клиентами.

Теперь вы можете задаться вопросом, почему компилятор не может определить тип jamesи bob, и при этом предупредить нас? Иногда это возможно, но если переменные действительно не имеют типа, то мы можем сделать следующее:

var james;
var bob;
if (getRandomNumber() > 0.5) {
   james = new Customer();
   bob = new Employee();
} else {
   james = new Employee();
   bob = new Customer();
}

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

В целом, языки с динамической типизацией используются для языков сценариев, где нет этапа компиляции, и поэтому ошибок компиляции не существует, а это означает, что дополнительные нажатия клавиш, необходимые для задания типа переменных, не будут очень полезными.

У динамически типизированных языков также есть некоторые явные преимущества, в основном с точки зрения меньшего количества кода, необходимого для реализации одного и того же проекта: интерфейсы не должны быть написаны, потому что все «типизировано по типу утки» (нас интересует только то, какие методы / свойства у объекта есть). , а не к какому классу принадлежит объект), переменным не нужно задавать явный тип ... с компромиссом, который мы узнаем о несколько меньшем количестве ошибок, прежде чем мы начнем запускать наш код.

Теодор Мердок
источник
Спасибо, Теодор. «Но если компилятор не знает типы переменных, он не может предупредить вас о том, что вы сделали недопустимый вызов». Как вы упомянули, компилятор может знать типы объектов, на которые указывают переменные.
sturdytree
Теодор: В вашем примере, james / bob, мы, как программисты, должны знать, как мы использовали наши переменные (и хорошее именование помогает), и поэтому я не вижу проблем с этим. Когда вы говорите «мы не всегда можем знать тип переменной», я предполагаю, что вы имеете в виду, что мы не всегда можем знать тип объекта, на который указывает переменная, но компилятор может решить эту проблему и поэтому предупреждает о неправильных членах, являющихся применяется (то есть мы можем иметь статическую проверку).
sturdytree
2
Выше приведен пример, в котором вы не можете знать типы объектов статически ... в зависимости от пути выполнения, тип объекта, хранящегося в переменной без информации о типе, может отличаться. У вас может быть такой язык, как C #, в котором можно получить информацию о типе во время компиляции, но, насколько я знаю, не существует языка как со статической проверкой типов, так и с переменными по-настоящему типами, вычислительная сложность статического анализа также вероятна Великий.
Теодор Мердок
Теодор, спасибо, ты правильно понял, что я говорю о языке со статической проверкой типов (основанной на типе объекта) и переменными без типов. Грустно слышать, что их нет - мне сказали, что в Python есть переменные без типов, но похоже, что в нем нет статической проверки типов.
sturdytree
1
-1; сильная / слабая типизация ортогональна статической / динамической типизации. C статически слабо типизирован; Lisp и Python динамически строго типизированы.
Фред Фу
5

Таким образом, профессиональные программисты не должны выяснять,

10 + "10"

is "1010" or 20....

Что это за ошибка, во время компиляции со статически типизированным языком или во время выполнения с динамически типизированным. Ну, в любом случае, вменяемые.

Тони Хопкинсон
источник
2
Т.е. Perl не вменяемый язык? :)
Фред Фу
5
@larsmans: на самом деле нет, это не так. но это чисто мнение.
NotMe
2
@ChrisLively: я рад, что это факт. Пожалуйста, приходите ко мне на работу в любое время, чтобы убедить моих коллег, любящих Perl;)
Фред Фу,
2
10 + «10» использует оператор «+» для объекта типа integer и объекта типа string. Компилятор выдаст ошибку. Мой вопрос касается типа переменной, а не объекта.
sturdytree
2
Он является действительным C: Это указатель арифметика.
Ден04
4

Предполагая, что у вас есть переменная one(установлена ​​в 1) и вы пытаетесь оценить one + one. Если вы не имели представления о типе, то 1 + 1 будет неоднозначным. Вы можете утверждать, что 2 или 11 могут быть правильными ответами. Это становится неоднозначным, если контекст не задан.

Я видел это происходит в SQLite , где типы баз данных неумышленно установлены в VARCHARвместо INTи когда были сделаны операции люди получали неожиданные результаты.

В c #, если контекст выводит тип, вы можете использовать varключевое слово.

var c = new Customer();
var e = new Employer();

Скомпилирует c и e с предполагаемыми типами во время компиляции.

Стивен Куан
источник
1
В Python 1 + 1всегда есть тип int, но нет необходимости объявлять это. Вопрос в том, почему переменные имеют тип, а не значения .
Фред Фу
Жаль , что вы должны были видеть , variablesне valuesкогда я 1 + 1в моем примере. Я думаю, это не было ясно.
Стивен Куан
Тем не менее, динамически типизированные языки могут справиться с этим. В Python one=1; print(one+one)печатает 2. one="1"; print(one+one)отпечатки 11. Пример SQLite более убедителен, но проблема заключается в слабой типизации, поэтому он не очень актуален для C #.
Фред Фу
В моей предложенной схеме один = 1 означает переменную, указывающую на целочисленный тип (я не предлагаю вообще никаких типов - у объектов будет тип). один + один не был бы двусмысленным (мы добавляем два целочисленных объекта / значения)
sturdytree
1
Привет @ dan04, я видел это по ORDER BYнеосторожности на VARCHARполе. См. Stackoverflow.com/questions/9103313/… .
Стивен Куан
4

Переменная не должна иметь связанный тип. К таким языкам относятся Лисп, Схема, Эрланг, Пролог, Smalltalk, Perl, Python, Ruby и другие.

Переменная также может иметь тип, но вам может не потребоваться записать тип в программе. Это обычно называется выводом типа. ML, Haskell и их потомки имеют мощный вывод типа; некоторые другие языки имеют его в меньших формах, таких как объявления C ++ auto.

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

Райан Калпеппер
источник
1

Когда вы определяете тип, который представляет ваша переменная, вы делаете заявление о нескольких вещах. Вы определяете требования к выделению памяти для своей переменной и определяете правила совместимости и диапазона для своей переменной. Он позволяет избежать путаницы в отношении ваших намерений в отношении данных, которые вы храните, и предоставляет вам относительно дешевые средства для выявления потенциальных проблем в вашем коде во время компиляции.

Если вы объявите следующие переменные:

myVar      = 5;
myOtherVar = "C";

Что вы можете сделать из этих переменных? Является myVarзнаком или без знака? Это 8-битный, 64-битный или что-то среднее? Является myOtherVarли String (фактически массив) или Char? Это ANSI или Unicode?

Предоставляя конкретные типы данных, вы предоставляете компилятору подсказки о том, как он может оптимизировать требования к памяти для вашего приложения. Некоторые языки не сильно беспокоятся об этом, позволяя решать эти вопросы во время выполнения, тогда как другие языки допускают определенное количество динамической типизации, потому что, анализируя код, можно определить типы данных.

Еще один момент, связанный со строго типизированными языками, заключается в том, что он избавляет вас от необходимости давать инструкции компилятору каждый раз, когда вы используете переменную. Можете ли вы представить, насколько ужасным и нечитабельным стал бы ваш код, если бы каждый раз, когда вы обращались к переменной, вы были вынуждены эффективно приводить ее, чтобы сообщить компилятору, какой это тип значения? !!

S.Robins
источник
Хорошая мысль, хотя компилятор может просто использовать наиболее эффективный тип (например, small int), основанный на значении, я могу видеть, что мы можем хотеть, чтобы "C" был строковым объектом, а не char, чтобы иметь возможность выполнять определенные операции , Однако в таких случаях мы можем просто указать a = (string) "C". Это создает строковый объект, и «a» (нетипизированная переменная) просто указывает на него. Я не считаю это более ужасным, чем строка a = "C";
sturdytree
0

Компьютерная программа - это граф узлов процесса, описывающий, что должна делать «машина», представленная языковой средой исполнения (в большинстве случаев расширенной с помощью наборов инструментов), в каком порядке или при каких условиях. Этот график представлен текстовым файлом (или набором текстовых файлов), написанным на определенном языке и (частично или полностью) созданным, когда компилятор / интерпретатор читает (десериализует) этот файл. Кроме того, есть некоторые среды (UML или инструменты для создания графических программ), где вы можете построить этот график и сгенерировать исходный код на целевом языке.

Почему я это говорю? Потому что это приводит к ответу на ваш вопрос.

Текст вашей программы - это ваши указания о том, как компьютер должен решать реальную задачу, содержащий как этапы процесса (условия, действия), так и структуру (какие компоненты вы используете в решении). Последнее означает, что вы получаете или создаете некоторые экземпляры других компонентов, помещаете их в именованные блоки (переменные) и используете их: обращаетесь к их данным и услугам.

В некоторых языках есть единообразные блоки, в которых важна только метка, но вы можете поместить в них что угодно, вы даже можете использовать переменную с именем «target» для хранения «Person» в начале и «Car» в конце тот же алгоритм. Другие требуют, чтобы вы создали «фасонные» боксы, поэтому разные для человека или автомобиля - хотя они по-прежнему позволяют создавать «универсальный бокс» (Java Object, C / C ++ void *, Objective C «id» ...) и брось как хочешь. Типизированные языки позволяют вам выразить свою структуру в мельчайших деталях, создавая «контракты типов» для ваших переменных (хотя вы можете обойти это ограничение), в то время как нетипизированные языки поддерживают этот подход «я наверняка буду знать, что я поместил в этот блок в этот раз» по умолчанию и единственное поведение.

Оба подхода жизнеспособны, имеют свой интеллект компилятора, много книг по программированию, практики и фреймворки, написанные с их использованием (и другие тонны книг об этих фреймворках) на разных уровнях. Таким образом, сегодня ответ кажется скорее вопросом вкуса и знаний действительной команды программистов, чем правильно обоснованным, взвешенным и проверенным утверждением о том, использовать или не использовать типы.

Я думаю, что нет необходимости говорить, что я предпочитаю правила трюкам, особенно для долгосрочных, больших командных проектов (иначе говоря, «серьезных»). Причина: насколько я знаю, наиболее вероятные причины неудачи / проскальзывания проекта ПО: неясные требования и плохой дизайн (80%! По моим исследованиям), и только несколько процентов остается для фактического кодирования. Все правила и контракты обеспечивают более четкое проектирование, продуманность и требуют решений, которые должны быть приняты ранее и соответствующими людьми. Типы означают правила: меньше «свободы» и «крутости» - больше подготовки, мышления, стандартов кодирования, контролируемой командной работы. Для меня это звучит как необходимый фактор успеха, а также «дом, милый дом».

Мои 2 цента.

Лоран Кедвес
источник
-3

AFIAK все языки с динамической типизацией являются интерпретируемыми языками. Это уже очень неэффективно, добавление неэффективности динамической типизации не будет большой потерей времени. Однако скомпилированный язык не будет ссылаться на вещи по имени, когда он работает. (Исключая случайное использование отражения .net и т. П. - функций, которые очень медленны по сравнению с базовым языком.) Поиск всех этих имен будет медленным, медленным, медленным.

Лорен Печтель
источник
3
Вы знаете, неправильно. Существует множество компиляторов для языков с динамической типизацией, и некоторые из них довольно быстрые. Примеры включают Common Lisp, Scheme и Erlang.
Райан Калпеппер
3
Не существует такого понятия, как «интерпретируемый язык». Язык - это абстрактный набор математических правил. Язык не компилируется и не интерпретируется. Язык просто есть. Компиляция и интерпретация являются чертами реализации, а не языка. Каждый язык может быть реализован с помощью интерпретатора, и каждый язык может быть реализован с помощью компилятора. И почти каждый язык имеет как интерпретируемые, так и скомпилированные реализации, например, есть интерпретаторы для C и компиляторы для ECMAScript, Ruby и Python.
Йорг Миттаг
-4

Динамически типизированные языки часто рекламируются как «объектно-ориентированные». Они не. Они могут быть ориентированы на инкапсуляцию, но никогда не объектно-ориентированы. Ориентация на объект - это все о типах.

«Мальчик едет на велосипеде своего брата в продуктовый магазин и покупает булку хлеба у бакалейщика». Используя объектную ориентацию, можно сразу написать набор классов (типов) для описания этого реального сценария.

В динамически типизированном языке сценарий может быть представлен только так:

«Объект ездит на объекте своего объекта к объекту и покупает объект у объекта».

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

Статическая типизация обеспечивает лучшую эффективность кодирования, возможность повторного использования и удобство обслуживания, потому что интегрированные среды разработки знают типы переменных. Зная типы переменных, IDE может обеспечить автоматическое завершение, так что программисту не нужно возвращаться к определению класса, чтобы запомнить, было ли свойство члена записано как «backlightControl» или «backLightControl» или «bkLightCtrl».

Статическая типизация допускает автоматический рефакторинг, поскольку среда IDE знает все места, где переменная содержит экземпляр рефакторинга объекта.

Статическая типизация допускает повторное использование и ремонтопригодность. Динамическая печать лучше подходит для одноразового кода. Предположим, новый разработчик приходит с улицы и смотрит на существующий фрагмент кода. Если код статически типизирован, разработчик может в два клика мыши проверить определение класса каждой из задействованных переменных, знает, для чего предназначен класс, знает, какие другие методы и свойства доступны. Если код динамически типизирован, разработчик должен использовать глобальный поиск, чтобы выяснить, что происходит.

Чарли Хэнд
источник
2
Боюсь, я не согласен с вами по первой части вашего аргумента. Языки с динамической типизацией обычно являются «объектно-ориентированными», но не «ориентированными на классы». Это важное различие. В вашем примере вы на самом деле живете в иллюзии. У вас может быть Boyкласс, но я сомневаюсь, что он может делать все, что в реальном мире делает "мальчик". Однако в вашем динамическом примере (объект едет ...) мы знаем единственную важную вещь об этом "мальчишеском" объекте - он может ездить . Это основная философия языков динамического типа. У него есть + ve s и -ve s. Какой вам нравится, ваше мнение.
Чип