Что такое Type-safe?

Ответы:

247

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

Несколько простых примеров:

// Fails, Trying to put an integer in a string
String one = 1;
// Also fails.
int foo = "bar";

Это также относится к аргументам методов, поскольку вы передаете им явные типы:

int AddTwoNumbers(int a, int b)
{
    return a + b;
}

Если бы я попытался вызвать это с помощью:

int Sum = AddTwoNumbers(5, "5");

Компилятор выдаст ошибку, потому что я передаю строку («5»), и она ожидает целое число.

На языке со свободной типизацией, таком как javascript, я могу сделать следующее:

function AddTwoNumbers(a, b)
{
    return a + b;
}

если я назову это так:

Sum = AddTwoNumbers(5, "5");

Javascript автоматически преобразует 5 в строку и возвращает «55». Это связано с тем, что javascript использует знак + для конкатенации строк. Чтобы сделать это с учетом типов, вам нужно сделать что-то вроде:

function AddTwoNumbers(a, b)
{
    return Number(a) + Number(b);
}

Или, возможно:

function AddOnlyTwoNumbers(a, b)
{
    if (isNaN(a) || isNaN(b))
        return false;
    return Number(a) + Number(b);
}

если я назову это так:

Sum = AddTwoNumbers(5, " dogs");

Javascript автоматически преобразует 5 в строку и добавляет их, чтобы вернуть «5 собак».

Не все динамические языки так просты, как javascript (фактически динамический язык не подразумевает свободный типизированный язык (см. Python)), некоторые из них на самом деле приведут к ошибке времени выполнения при неверном приведении типов.

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

Теперь вернемся к C # ...

C # поддерживает языковую функцию под названием ковариация , это в основном означает, что вы можете заменить базовый тип дочерним типом и не вызвать ошибку, например:

 public class Foo : Bar
 {
 }

Здесь я создал новый класс (Foo), который подклассы Bar. Теперь я могу создать метод:

 void DoSomething(Bar myBar)

И вызовите его, используя Foo или Bar в качестве аргумента, оба будут работать, не вызывая ошибки. Это работает, потому что C # знает, что любой дочерний класс Bar будет реализовывать интерфейс Bar.

Тем не менее, вы не можете сделать обратное:

void DoSomething(Foo myFoo)

В этой ситуации я не могу передать Bar этому методу, потому что компилятор не знает, что Bar реализует интерфейс Foo. Это потому, что дочерний класс может (и обычно будет) сильно отличаться от родительского класса.

Конечно, теперь я вышел далеко за пределы первоначального вопроса, но это все, что нужно знать :)

FlySwat
источник
26
Я чувствую, что этот ответ неверен: безопасность типов не обязательно обеспечивается во время компиляции. Я понимаю, что, например, Scheme считается безопасным типом, но проверяется динамически (безопасность типов обеспечивается во время выполнения). Это в основном перефразирует введение в типы и языки программирования Бенджамина С. Пирса.
Николас Ринаудо
11
То, что вы описываете, называется полиморфизмом, а не ковариацией. Ковариация используется в дженериках.
IllidanS4 хочет вернуть Монику
@NicolasRinaudo отметьте, что разрыв между динамическими языками и статическими сокращается динамической компиляцией и предварительной компиляцией для «интерпретируемых» языков и отражением в «скомпилированных» языках. Например, Reflection позволяет вводить утки во время выполнения, поэтому скомпилированный язык может сказать «эй, у него есть метод Quack (), я его назову и посмотрю, что произойдет». Подобные Паскалю языки также часто имеют (необязательную) проверку переполнения во время выполнения, что приводит к тому, что те ошибки «компилятора», возникающие во время выполнения, «не могут поместиться в целочисленное значение, поставляемое в 8-битном месте назначения {core dump}».
Код
2
Ваш пример ссылается на концепцию под названием «строго типизированный», которая не совпадает с безопасностью типов. Безопасность типов - это когда язык может обнаруживать ошибки типов во время выполнения или компиляции. Например, Python слабо набран и безопасен. Этот ответ должен быть помечен как вводящий в заблуждение.
Дантебарба
объяснение в целом v. хорошо, но безопасность типов не такая, как у строго типизированных
senseiwu
57

Безопасность типов не следует путать со статической / динамической типизацией или строгой / слабой типизацией.

Безопасный по типу язык - это язык, в котором единственными операциями, которые можно выполнять с данными, являются те, которые оправдываются типом данных. То есть, если ваши данные имеют тип Xи Xне поддерживают операции y, то язык не позволит вам выполнить y(X).

Это определение не устанавливает правила, когда это проверено. Это может быть во время компиляции (статическая типизация) или во время выполнения (динамическая типизация), обычно через исключения. Это может быть и то и другое: некоторые статически типизированные языки позволяют вам преобразовывать данные из одного типа в другой, и достоверность приведений должна проверяться во время выполнения (представьте, что вы пытаетесь преобразовать Objectв a Consumer- компилятор не имеет способ узнать, является ли это приемлемым или нет).

Безопасность типов не обязательно означает строго типизированный - некоторые языки общеизвестно слабо типизированы, но все еще, вероятно, безопасны для типов. Взять, к примеру, Javascript: его система типов так же слаба, как и они, но все же строго определена. Это позволяет автоматически приводить данные (скажем, строки в целые числа), но в рамках четко определенных правил. Насколько мне известно, ни один случай, когда программа Javascript будет вести себя неопределенным образом, и если вы достаточно умны (я не), вы должны быть в состоянии предсказать, что произойдет при чтении кода Javascript.

Примером небезопасного языка программирования является C: чтение / запись значения массива вне границ массива имеет неопределенное поведение по спецификации . Невозможно предсказать, что произойдет. C - это язык, который имеет систему типов, но не является безопасным для типов.

Николя Ринаудо
источник
1
каковы другие примеры небезопасных языков? Что вы подразумеваете под «записью значения массива за пределы массива, поведение которого по спецификации не определено. Предсказать, что произойдет, невозможно». Как и Javascript, он вернет неопределенное право, верно? Или действительно все может случиться. Можете ли вы привести пример этого?
ARK
1
@AkshayrajKore конечно. Массивы - это указатели памяти, поэтому, записывая за пределами границ, вы можете перезаписать данные другой программы - которые ничего не могут сделать, вывести программу из строя, вызвать ее стирание на жестком диске - она ​​не определена и зависит от того, кто читает этот бит памяти и как это отреагирует на это.
Николя Ринаудо
@ Николас Ринаудо: Это не правильно. Вы должны прочитать о виртуальной памяти. Каждый процесс имеет свое собственное виртуальное адресное пространство, поэтому процесс не может «перезаписать данные другой программы» таким образом.
ilstam
Вы правы - это должно было прочитать, что вы, возможно, перезаписываете другую часть памяти вашей программы - вплоть до, я полагаю, самой программы?
Николас Ринаудо
@NicolasRinaudo Кодовый сегмент программы отображается только для чтения в виртуальном адресном пространстве. Так что, если вы попытаетесь написать в него, это вызовет ошибку сегментации, и ваша программа потерпит крах. А также, если вы попытаетесь записать в не отображенную память, это вызовет сбой страницы и снова произойдет сбой. Однако, если вам не повезло, вы можете просто перезаписать данные из стека или кучи процесса (например, другие переменные или другие вещи). В этом случае вы, вероятно, не сразу же потерпите крах, что еще хуже, потому что вы не заметите ошибку до (надеюсь) позже!
ilstam
32

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

Есть 2 основных вопроса, связанных с безопасностью типов. Память ** и тип данных (с соответствующими операциями).

Объем памяти**

charОбычно A требует 1 байт на символ или 8 бит (зависит от языка, в Java и C # хранятся символы Unicode, для которых требуется 16 бит). intТребуется 4 байта, или 32 бита (обычно).

Визуально:

char: |-|-|-|-|-|-|-|-|

int : |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-|

Безопасный по типу язык не позволяет вставлять int в char во время выполнения (это должно привести к исключению типа из класса или из памяти). Однако на небезопасном языке типа вы перезаписываете существующие данные еще на 3 смежных байта памяти.

int >> char:

|-|-|-|-|-|-|-|-| |?|?|?|?|?|?|?|?| |?|?|?|?|?|?|?|?| |?|?|?|?|?|?|?|?|

В приведенном выше случае 3 байта справа перезаписываются, поэтому любые указатели на эту память (скажем, 3 последовательных символа), которые ожидают получить предсказуемое значение символа, теперь будут иметь мусор. Это вызываетundefined поведение в вашей программе (или, что еще хуже, возможно, в других программах, в зависимости от того, как ОС выделяет память - очень маловероятно в наши дни).

** Хотя эта первая проблема технически не относится к типу данных, языки с безопасными типами обращаются к ней по своей сути, и она визуально описывает проблему для тех, кто не знает, как «выглядит» выделение памяти.

Тип данных

Более тонкий и прямой тип проблемы заключается в том, что два типа данных используют одинаковое распределение памяти. Возьмите int против неподписанного int. Оба 32 бит. (Точно так же легко могут быть char [4] и int, но более распространенной проблемой является uint vs. int).

|-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-|

|-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-|

Небезопасный язык типов позволяет программисту ссылаться на правильно распределенный интервал из 32 битов, но когда значение беззнакового целого читается в пространстве целого (или наоборот), мы снова имеем undefinedповедение. Представьте себе проблемы, которые это может вызвать в банковской программе:

"Чувак! Я переиграл 30 долларов, и теперь у меня осталось 65 506 долларов !!"

... Конечно, банковские программы используют гораздо большие типы данных. ;) РЖУНИМАГУ!

Как уже отмечали другие, следующая проблема - вычислительные операции над типами. Это уже достаточно покрыто.

Скорость против безопасности

Большинству программистов сегодня не нужно беспокоиться о таких вещах, если они не используют что-то вроде C или C ++. Оба этих языка позволяют программистам легко нарушать безопасность типов во время выполнения (прямое обращение к памяти), несмотря на все усилия компиляторов минимизировать риск. ОДНАКО, это не все плохо.

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

Gr3go
источник
27

Многие ответы здесь связывают безопасность типов со статической и динамической типизацией. Динамически типизированный язык (например, smalltalk) также может быть безопасным для типов.

Короткий ответ: язык считается безопасным от типа, если ни одна операция не приводит к неопределенному поведению. Многие считают, что требование явных преобразований типов необходимо для строгой типизации языка, поскольку автоматические преобразования могут иногда приводить к четко определенному, но неожиданному / неинтуитивному поведению.

ididak
источник
1
Подождите, ваше определение типа-безопасности не имеет ни единого слова «типа»: D if no operation leads to undefined behavior.
Василий Новиков
1
Также я бы не согласился с таким определением. Я думаю, что безопасность типов означает ровно 1. существование типов 2. знание их компилятору и соответствующие проверки, конечно.
Василий Новиков
10

Язык программирования, «типобезопасный», означает следующее:

  1. Вы не можете читать из неинициализированных переменных
  2. Вы не можете индексировать массивы за их пределами
  3. Вы не можете выполнять непроверенные приведения типа
Кекуле
источник
8

Объяснение от специалиста по гуманитарным наукам, а не по специальности:

Когда люди говорят, что язык или языковая функция безопасна от типа, они подразумевают, что язык поможет предотвратить, например, передачу чего-то, что не является целым числом, некоторой логике, которая ожидает целое число.

Например, в C # я определяю функцию как:

 void foo(int arg)

Компилятор остановит меня от этого:

  // call foo
  foo("hello world")

В других языках компилятор не остановит меня (или нет компилятора ...), поэтому строка будет передана в логику, и тогда, вероятно, произойдет что-то плохое.

Типы безопасных языков пытаются поймать больше во время компиляции.

С другой стороны, с безопасными типами языков, когда у вас есть строка типа «123», и вы хотите работать с ней как с типом int, вам нужно написать больше кода для преобразования строки в тип int или когда у вас есть тип int как 123 и хотите использовать его в сообщении типа «ответ 123», вам нужно написать больше кода для преобразования / приведения его в строку.

Кори Трагер
источник
4
Майор гуманитарных наук сказал бы объяснение :) Вы также смешиваете статическую типизацию и динамическую типизацию.
ididak
1
Либеральные искусства "майоры", а не "майоры".
Кори Трагер
5

Чтобы получить лучшее понимание, посмотрите видео ниже, где демонстрируется код на безопасном языке типа (C #), а НЕ на безопасном языке типа (javascript).

http://www.youtube.com/watch?v=Rlw_njQhkxw

Теперь для длинного текста.

Безопасность типов означает предотвращение ошибок типов. Ошибка типа происходит, когда тип данных одного типа присваивается другому типу НЕИЗВЕСТНО, и мы получаем нежелательные результаты.

Например, JavaScript не является типобезопасным языком. В приведенном ниже коде «num» является числовой переменной, а «str» является строкой. Javascript позволяет мне делать «num + str», теперь GUESS будет выполнять арифметику или конкатенацию.

Теперь для приведенного ниже кода результат «55», но важным моментом является путаница, возникшая в результате того, какую операцию он будет выполнять.

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

<script>
var num = 5; // numeric
var str = "5"; // string
var z = num + str; // arthimetic or concat ????
alert(z); // displays  “55”
</script>

C # является безопасным типом языка. Это не позволяет одному типу данных быть назначенным другому типу данных. Приведенный ниже код не позволяет использовать оператор «+» для разных типов данных.

введите описание изображения здесь

Шивпрасад Койрала
источник
4

Безопасный тип означает, что программно тип данных для переменной, возвращаемого значения или аргумента должен соответствовать определенным критериям.

На практике это означает, что 7 (целочисленный тип) отличается от «7» (символ в кавычках строкового типа).

PHP, Javascript и другие динамические скриптовые языки обычно слабо типизированы, так как они преобразуют (строку) «7» в (целое число) 7, если вы попытаетесь добавить «7» + 3, хотя иногда вам приходится это делать явно (и Javascript использует символ «+» для объединения).

C / C ++ / Java не поймет этого или объединит результат в «73». Безопасность типов предотвращает эти типы ошибок в коде, делая требование типа явным.

Безопасность типов очень полезна. Решением вышеупомянутых «7» + 3 было бы набрать cast (int) «7» + 3 (равно 10).

Джаред Фарриш
источник
3

Концепция:

Чтобы быть очень простым Type Safe как значения, он гарантирует, что тип переменной должен быть безопасным, как

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

так что все дело в безопасности типов вашего хранилища в терминах переменных.

azizsagi
источник
2

Попробуйте это объяснение на ...

TypeSafe означает, что переменные статически проверяются для соответствующего назначения во время компиляции. Например, передайте строку или целое число. Эти два разных типа данных не могут быть перекрестно назначены (т. Е. Вы не можете назначить целое число для строки и не можете назначить строку для целого числа).

Для нетипичного поведения, рассмотрите это:

object x = 89;
int y;

если вы попытаетесь сделать это:

y = x;

Компилятор выдает ошибку, которая говорит, что он не может преобразовать System.Object в Integer. Вы должны сделать это явно. Одним из способов будет:

y = Convert.ToInt32( x );

Приведенное выше задание не является безопасным. Назначение типов безопасности - это когда типы могут быть непосредственно назначены друг другу.

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

Например:

Session[ "x" ] = 34;

работает отлично. Но чтобы вернуть целочисленное значение обратно, вам необходимо:

int i = Convert.ToInt32( Session[ "x" ] );

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

C # - это безопасный язык, но следите за статьями о C # 4.0; вырисовываются интересные динамические возможности (хорошо ли, что C # по сути получает Option Strict: Off ... посмотрим).

гр.
источник
Лично я ненавижу нотацию Convert.To, почему бы вам не использовать безопасное приведение? Это только меньший вызов функции в стеке вызовов.
FlySwat
2

Безопасный тип - это код, который обращается только к тем ячейкам памяти, к которым он имеет доступ, и только четко определенными, допустимыми способами. Типобезопасный код не может выполнить операцию с объектом, который недопустим для этого объекта. Компиляторы языка C # и VB.NET всегда генерируют код, безопасный для типов, который проверяется на то, что он является безопасным во время компиляции JIT.

Jonuz
источник
Вы имеете в виду безопасность памяти?
Голопот
1

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

Для многих переменных набор значений, которые могут быть назначены переменной, определяется во время написания программы. Например, переменной с именем «color» может быть разрешено принимать значения «red», «green» или «blue» и никогда не иметь никаких других значений. Для других переменных эти критерии могут изменяться во время выполнения. Например, переменная с именем «color» может принимать значения только в столбце «name» таблицы «Colors» в реляционной базе данных, где «red», «green» и «blue» - это три значения для «имени» в таблице «Цвета», но некоторая другая часть компьютерной программы может быть в состоянии добавить в этот список во время работы программы, и переменная может принять новые значения после того, как они добавлены в таблицу Цветов ,

Многие безопасные от типов языки создают иллюзию «безопасности типов», настаивая на строгом определении типов для переменных и позволяя переменной назначать только значения одного и того же «типа». Есть несколько проблем с этим подходом. Например, программа может иметь переменную yearOfBirth, которая представляет собой год рождения человека, и заманчиво привести его к типу короткого целого числа. Однако это не короткое целое число. В этом году это число меньше 2009 года и больше -10000. Тем не менее, этот набор увеличивается на 1 каждый год по мере выполнения программы. Делать это "коротким целым" недостаточно. Чтобы сделать эту переменную безопасной для типов, необходима функция проверки во время выполнения, которая гарантирует, что число всегда будет больше -10000 и меньше, чем в следующем календарном году.

Языки, которые используют динамическую типизацию (или типизацию утилит, или манифестную типизацию), такие как Perl, Python, Ruby, SQLite и Lua, не имеют понятия типизированных переменных. Это вынуждает программиста написать подпрограмму проверки времени выполнения для каждой переменной, чтобы гарантировать ее правильность или выдержать последствия необъяснимых исключений времени выполнения. По моему опыту, программисты на статически типизированных языках, таких как C, C ++, Java и C #, часто склоняются к мысли, что статически определенные типы - это все, что им нужно сделать, чтобы получить преимущества безопасности типов. Это просто неверно для многих полезных компьютерных программ, и трудно предсказать, верно ли это для какой-либо конкретной компьютерной программы.

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

Джей Годсе
источник
2
Переменные Python типизированы ( фактически строго типизированы). Попробуйте сделать это, например: «str» + 1. Вы получите ошибку. Однако типы проверяются во время выполнения, а не во время компиляции.
Mipadi