O que é seguro para tipos?

Respostas:

247

Segurança de tipo significa que o compilador validará os tipos durante a compilação e lançará um erro se você tentar atribuir o tipo errado a uma variável.

Alguns exemplos simples:

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

Isso também se aplica aos argumentos do método, pois você está passando tipos explícitos para eles:

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

Se eu tentasse chamar isso usando:

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

O compilador lançaria um erro, porque estou passando uma string ("5") e está esperando um número inteiro.

Em uma linguagem pouco digitada, como javascript, posso fazer o seguinte:

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

se eu chamar assim:

Sum = AddTwoNumbers(5, "5");

Javascript converte automaticamente o 5 em uma string e retorna "55". Isso ocorre devido ao javascript usando o sinal + para concatenação de string. Para torná-lo sensível ao tipo, você precisa fazer algo como:

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

Ou, possivelmente:

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

se eu chamar assim:

Sum = AddTwoNumbers(5, " dogs");

O Javascript converte automaticamente o 5 em uma string e os anexa para retornar "5 dogs".

Nem todas as linguagens dinâmicas são tão perdoadoras quanto o javascript (na verdade, uma linguagem dinâmica não implica implicitamente uma linguagem de tipo livre (consulte Python)), algumas delas realmente fornecem um erro de tempo de execução na conversão de tipo inválida.

Embora seja conveniente, ele abre muitos erros que podem ser facilmente perdidos e identificados apenas ao testar o programa em execução. Pessoalmente, prefiro que meu compilador me diga se eu cometi esse erro.

Agora, de volta ao C # ...

O C # suporta um recurso de idioma chamado covariância , isso basicamente significa que você pode substituir um tipo base por um tipo filho e não causar um erro, por exemplo:

 public class Foo : Bar
 {
 }

Aqui, criei uma nova classe (Foo) que subclasse Bar. Agora posso criar um método:

 void DoSomething(Bar myBar)

E chame-o usando um Foo ou um Bar como argumento, ambos funcionarão sem causar erro. Isso funciona porque o C # sabe que qualquer classe filho de Bar implementará a interface do Bar.

No entanto, você não pode fazer o inverso:

void DoSomething(Foo myFoo)

Nessa situação, não posso passar Bar para esse método, porque o compilador não sabe que Bar implementa a interface do Foo. Isso ocorre porque uma classe filho pode (e geralmente será) ser muito diferente da classe pai.

Claro, agora eu me afastei do fundo da questão e além do escopo da pergunta original, mas é tudo bom saber :)

FlySwat
fonte
26
Eu sinto que esta resposta está errada: a segurança do tipo não é necessariamente imposta no momento da compilação. Entendo que o esquema, por exemplo, é considerado seguro para o tipo, mas é verificado dinamicamente (a segurança do tipo é imposta no tempo de execução). Isso está parafraseando principalmente a introdução a Tipos e linguagens de programação, de Benjamin C. Pierce.
Nicolas Rinaudo 02/02
11
O que você descreve é ​​chamado polimorfismo, não covariância. A covariância é usada em genéricos.
IllidanS4 quer Monica de volta 16/04/2015
@NicolasRinaudo observa que a diferença entre linguagens dinâmicas e estáticas está sendo corroída pela compilação e pré-compilação dinâmicas para linguagens "interpretadas" e pela reflexão nas linguagens "compiladas". O Reflection permite a digitação em tempo de execução do pato, por exemplo, para que uma linguagem compilada possa dizer "ei, isso tem um método Quack (), chamarei isso e verei o que acontece". Os idiomas do tipo Pascal também costumam ter verificação de excesso de tempo de execução (opcional), levando a que os erros do "compilador" que acontecem no tempo de execução "não possam ajustar o número inteiro fornecido ao destino de 8 bits {core dump}".
Code Abominator
2
Seu exemplo faz referência a um conceito chamado "fortemente tipado" que não é o mesmo que segurança de tipo. Segurança de tipo é quando um idioma pode detectar erros de tipo na execução ou no tempo de compilação. O Python, por exemplo, é fracamente digitado e seguro. Esta resposta deve ser sinalizada, pois é muito enganadora.
Dantebarba 18/02/19
. explicação v geral bom, mas o tipo de segurança não é o mesmo que fortemente tipado
senseiwu
57

A segurança de tipo não deve ser confundida com tipagem estática / dinâmica ou tipagem forte / fraca.

Uma linguagem de segurança de tipo é aquela em que as únicas operações que se pode executar nos dados são aquelas que são toleradas pelo tipo de dados. Ou seja, se seus dados forem do tipo Xe Xnão suportarem operação y, o idioma não permitirá a execução y(X).

Esta definição não define regras quando isso está marcado. Pode ser em tempo de compilação (digitação estática) ou em tempo de execução (digitação dinâmica), geralmente por meio de exceções. Pode ser um pouco de ambos: algumas linguagens de tipo estaticamente permitem converter dados de um tipo para outro, e a validade das transmissões deve ser verificada em tempo de execução (imagine que você está tentando transmitir uma Objectpara um Consumer- o compilador não tem maneira de saber se é aceitável ou não).

Segurança de tipo também não significa necessariamente digitação forte - alguns idiomas são notoriamente fracamente digitados, mas ainda podem ser digitados com segurança. Tomemos o Javascript, por exemplo: seu sistema de tipos é tão fraco quanto possível, mas ainda é estritamente definido. Ele permite a transmissão automática de dados (digamos, cadeias de caracteres para ints), mas dentro de regras bem definidas. Pelo que sei, nenhum caso em que um programa Javascript se comportará de maneira indefinida e, se você for esperto o suficiente (não sou), poderá prever o que acontecerá ao ler o código Javascript.

Um exemplo de uma linguagem de programação insegura é C: a leitura / gravação de um valor de matriz fora dos limites da matriz tem um comportamento indefinido por especificação . É impossível prever o que vai acontecer. C é uma linguagem que possui um sistema de tipos, mas não é um tipo seguro.

Nicolas Rinaudo
fonte
1
Quais são outros exemplos de linguagens inseguras? O que você quer dizer com "escrever um valor da matriz fora dos limites da matriz tem um comportamento indefinido por especificação. É impossível prever o que acontecerá". Como o Javascript, ele retornará indefinido, certo? Ou realmente qualquer coisa pode acontecer. Você pode dar um exemplo disso?
ARK
1
@AkshayrajKore sure. As matrizes são ponteiros de memória; portanto, ao escrever fora dos limites, você pode substituir os dados de outro programa - o que não pode fazer nada, travar o programa, fazer com que ele apague seu disco rígido - é indefinido e depende de quem está lendo esse pedaço de memória e como ele reagirá a isso.
Nicolas Rinaudo 22/02
@Nicolas Rinaudo Isso não está correto. Você deve ler sobre memória virtual. Cada processo possui seu próprio espaço de endereço virtual, portanto, um processo não pode "substituir os dados de outro programa" dessa maneira.
ilstam
Você está correto - isso deve ter lido que você pode estar substituindo outra parte da memória do seu programa - incluindo, acredito, o próprio programa?
Nicolas Rinaudo
@NicolasRinaudo O segmento de código do programa é mapeado como somente leitura no espaço de endereço virtual. Portanto, se você tentar escrever nele, isso causaria uma falha de segmentação e seu programa falharia. Além disso, se você tentasse gravar na memória não mapeada, isso causaria uma falha na página e travaria novamente. No entanto, se você tiver azar, poderá substituir os dados da pilha ou pilha do processo (como outras variáveis ​​ou outras coisas). Nesse caso, você provavelmente não travaria imediatamente, o que é ainda pior, porque você não notará o bug até (espero) mais tarde!
ilstam
32

A segurança de tipo não é apenas uma restrição de tempo de compilação, mas uma restrição de tempo de execução . Sinto que, mesmo depois de todo esse tempo, podemos acrescentar mais clareza a isso.

Existem 2 questões principais relacionadas ao tipo de segurança. Memória ** e tipo de dados (com suas operações correspondentes).

Memória**

Um charrequer tipicamente 1 byte por caractere, ou 8 bits (depende da linguagem, Java e C # armazenar caracteres Unicode que exigem 16 bits). Um intrequer 4 bytes ou 32 bits (geralmente).

Visualmente:

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

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

Uma linguagem segura de tipo não permite que um int seja inserido em um char em tempo de execução (isso deve gerar algum tipo de exceção de classe ou de falta de memória). No entanto, em uma linguagem não segura do tipo, você substituirá os dados existentes em mais 3 bytes de memória adjacentes.

int >> char:

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

No caso acima, os 3 bytes à direita são substituídos; portanto, qualquer ponteiro para a memória (digamos 3 caracteres consecutivos) que espere obter um valor previsível de caracteres agora terá lixo. Isso causa undefinedcomportamento no seu programa (ou pior, possivelmente em outros programas, dependendo de como o SO aloca memória - muito improvável atualmente).

** Embora esse primeiro problema não seja tecnicamente sobre o tipo de dados, os idiomas seguros do tipo o endereçam de maneira inerente e descrevem visualmente o problema para aqueles que desconhecem a aparência da alocação de memória.

Tipo de dados

O problema de tipo mais sutil e direto é onde dois tipos de dados usam a mesma alocação de memória. Tome um int vs um int não assinado. Ambos são 32 bits. (Com a mesma facilidade, poderia ser um caractere [4] e um int, mas o problema mais comum é uint vs. int).

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

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

Uma linguagem não segura do tipo permite que o programador faça referência a um intervalo de 32 bits alocado corretamente, mas quando o valor de um int não assinado é lido no espaço de um int (ou vice-versa), novamente temos undefinedcomportamento. Imagine os problemas que isso pode causar em um programa bancário:

"Cara! Eu cheguei a mais de 30 dólares e agora tenho 65.506!"

... 'claro, os programas bancários usam tipos de dados muito maiores. ;) RI MUITO!

Como outros já apontaram, a próxima questão são operações computacionais em tipos. Isso já foi suficientemente coberto.

Velocidade vs Segurança

Atualmente, a maioria dos programadores nunca precisa se preocupar com essas coisas, a menos que estejam usando algo como C ou C ++. Ambas as linguagens permitem que os programadores violem facilmente a segurança de tipo em tempo de execução (referência direta à memória), apesar dos melhores esforços dos compiladores para minimizar o risco. No entanto, isso não é de todo ruim.

Uma das razões pelas quais essas linguagens são tão rápidas em termos de computação é que elas não são sobrecarregadas pela verificação da compatibilidade de tipos durante operações em tempo de execução, como, por exemplo, Java. Eles assumem que o desenvolvedor é um bom ser racional que não adiciona uma string e um int juntos e, por isso, o desenvolvedor é recompensado com velocidade / eficiência.

Gr3go
fonte
27

Muitas respostas aqui combinam segurança de tipo com tipagem estática e dinâmica. Uma linguagem digitada dinamicamente (como smalltalk) também pode ser segura para o tipo.

Uma resposta curta: um idioma é considerado seguro para o tipo se nenhuma operação levar a um comportamento indefinido. Muitos consideram o requisito de conversões explícitas de tipo, necessárias para que um idioma seja estritamente digitado, pois as conversões automáticas às vezes podem levar a comportamentos bem definidos, mas inesperados / não intuitivos.

ididak
fonte
1
Espere, sua definição de tipo de segurança não tem uma única palavra "type": D if no operation leads to undefined behavior.
VasiliNovikov 02/09/2014
1
Além disso, eu discordaria dessa definição. Eu acho que segurança de tipo significa exatamente 1. a existência de tipos 2. o conhecimento deles para o compilador e as verificações apropriadas, é claro.
VasiliNovikov 02/09/2014
10

Uma linguagem de programação que é 'type-safe' significa o seguinte:

  1. Você não pode ler de variáveis ​​não inicializadas
  2. Você não pode indexar matrizes além dos limites deles
  3. Você não pode executar conversões de tipo desmarcadas
Kekule
fonte
8

Uma explicação de um major de artes liberais, não de um major de ficção científica:

Quando as pessoas dizem que um idioma ou recurso de idioma é do tipo seguro, elas significam que o idioma ajudará a impedir que você, por exemplo, passe algo que não seja um número inteiro para alguma lógica que espera um número inteiro.

Por exemplo, em C #, defino uma função como:

 void foo(int arg)

O compilador então me impedirá de fazer isso:

  // call foo
  foo("hello world")

Em outros idiomas, o compilador não me parava (ou não há compilador ...), portanto, a string seria passada para a lógica e, provavelmente, algo ruim aconteceria.

Digite idiomas seguros, tente capturar mais em "tempo de compilação".

No lado negativo, com os idiomas de segurança de tipo, quando você tem uma string como "123" e deseja operá-la como um int, você precisa escrever mais código para converter a string em um int ou quando você tem um int como 123 e deseja usá-lo em uma mensagem como "A resposta é 123", é necessário escrever mais código para convertê-lo / convertê-lo em uma string.

Corey Trager
fonte
4
O especialista em artes liberais diria uma explicação :) Você também está combinando digitação estática e digitação dinâmica.
Idideck
1
Artes liberais "majors", não "major".
Corey Trager
5

Para entender melhor, assista ao vídeo abaixo, que demonstra o código em linguagem segura do tipo (C #) e NÃO em linguagem segura (javascript).

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

Agora, para o texto longo.

Segurança de tipo significa evitar erros de tipo. O erro de tipo ocorre quando um tipo de dado de um tipo é atribuído a outro tipo de maneira NÃO-CONHECIDA e obtemos resultados indesejáveis.

Por exemplo, o JavaScript não é uma linguagem segura de tipo. No código abaixo, “num” é uma variável numérica e “str” é uma string. Javascript me permite fazer "num + str", agora o GUESS faz aritmética ou concatenação.

Agora, para o código abaixo, os resultados são "55", mas o ponto importante é a confusão criada, que tipo de operação ele fará.

Isso está acontecendo porque o javascript não é uma linguagem segura de tipo. Está permitindo definir um tipo de dados para o outro tipo sem restrições.

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

C # é uma linguagem segura de tipo. Não permite que um tipo de dados seja atribuído a outro tipo de dados. O código abaixo não permite o operador "+" em diferentes tipos de dados.

insira a descrição da imagem aqui

Shivprasad Koirala
fonte
4

Tipo seguro significa que, programaticamente, o tipo de dados para uma variável, valor de retorno ou argumento deve caber dentro de um determinado critério.

Na prática, isso significa que 7 (um tipo inteiro) é diferente de "7" (um caractere citado do tipo string).

PHP, Javascript e outras linguagens de script dinâmico geralmente são de tipo fraco, pois convertem um (string) "7" em um (inteiro) 7 se você tentar adicionar "7" + 3, embora às vezes seja necessário fazer isso explicitamente (e o Javascript usa o caractere "+" para concatenação).

C / C ++ / Java não entenderá isso ou concatenará o resultado em "73". A segurança de tipo evita esses tipos de erros no código, explicitando o requisito de tipo.

A segurança do tipo é muito útil. A solução para o "7" + 3 acima seria digitar cast (int) "7" + 3 (igual a 10).

Jared Farrish
fonte
3

Conceito:

Para ser muito simples, digite Safe como os significados, garante que o tipo da variável seja seguro como

  1. nenhum tipo de dados errado, por exemplo, não é possível salvar ou inicializar uma variável do tipo string com número inteiro
  2. Índices fora do limite não estão acessíveis
  3. Permitir apenas o local específico da memória

portanto, é tudo sobre a segurança dos tipos de armazenamento em termos de variáveis.

azizsagi
fonte
2

Tente esta explicação em ...

TypeSafe significa que as variáveis ​​são verificadas estaticamente quanto à atribuição apropriada no momento da compilação. Por exemplo, considere uma sequência ou um número inteiro. Esses dois tipos de dados diferentes não podem ser atribuídos de forma cruzada (ou seja, não é possível atribuir um número inteiro a uma sequência nem atribuir uma sequência a um número inteiro).

Para um comportamento não tipificado, considere o seguinte:

object x = 89;
int y;

se você tentar fazer isso:

y = x;

o compilador gera um erro que diz que não pode converter um System.Object em um número inteiro. Você precisa fazer isso explicitamente. Uma maneira seria:

y = Convert.ToInt32( x );

A tarefa acima não é segura. Uma atribuição de segurança tipográfica é onde os tipos podem ser atribuídos diretamente um ao outro.

As coleções não tipificadas são abundantes no ASP.NET (por exemplo, coleções de aplicativos, sessões e estados de exibição). A boa notícia sobre essas coleções é que (minimizando várias considerações sobre gerenciamento de estado do servidor), você pode colocar praticamente qualquer tipo de dados em qualquer uma das três coleções. As más notícias: como essas coleções não são tipicamente seguras, você precisará converter os valores adequadamente quando os buscar novamente.

Por exemplo:

Session[ "x" ] = 34;

funciona bem. Mas para atribuir o valor inteiro de volta, você precisará:

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

Leia sobre os genéricos para saber como esse recurso o ajuda a implementar facilmente coleções typesafe.

C # é uma linguagem segura, mas observe os artigos sobre C # 4.0; possibilidades dinâmicas interessantes aparecem (é bom que o C # esteja essencialmente recebendo a opção Strict: Off ... veremos).

rp.
fonte
Pessoalmente, eu odeio a notação Convert.To, por que você não usa elenco seguro? É apenas menos chamada de função na pilha de chamadas.
FlySwat 4/11/08
2

O Type-Safe é um código que acessa apenas os locais de memória aos quais está autorizado a acessar, e somente de maneiras bem definidas e permitidas. O código de segurança de tipo não pode executar uma operação em um objeto inválido para esse objeto. Os compiladores de linguagem C # e VB.NET sempre produzem código de segurança de tipo, que é verificado como seguro durante a compilação JIT.

Jonuz
fonte
Você quer dizer segurança de memória?
golopot
1

Tipo seguro significa que o conjunto de valores que podem ser atribuídos a uma variável de programa deve atender a critérios bem definidos e testáveis. Variáveis ​​de tipo seguro levam a programas mais robustos, porque os algoritmos que manipulam as variáveis ​​podem confiar que a variável terá apenas um de um conjunto bem definido de valores. Manter essa confiança garante a integridade e a qualidade dos dados e do programa.

Para muitas variáveis, o conjunto de valores que podem ser atribuídos a uma variável é definido no momento em que o programa é gravado. Por exemplo, uma variável chamada "cor" pode assumir os valores "vermelho", "verde" ou "azul" e nunca outros valores. Para outras variáveis, esses critérios podem mudar no tempo de execução. Por exemplo, uma variável chamada "cor" só pode receber valores na coluna "nome" de uma tabela "Cores" em um banco de dados relacional, onde "vermelho", "verde" e "azul" são três valores para "name" na tabela "Colors", mas alguma outra parte do programa de computador poderá ser adicionada a essa lista enquanto o programa estiver em execução, e a variável poderá assumir os novos valores após serem adicionados à tabela Colors .

Muitas linguagens de segurança de tipo dão a ilusão de "segurança de tipo", insistindo na definição estrita de tipos para variáveis ​​e permitindo apenas que uma variável receba valores do mesmo "tipo". Existem alguns problemas com essa abordagem. Por exemplo, um programa pode ter uma variável "yearOfBirth", que é o ano em que uma pessoa nasceu, e é tentador transformá-la em um número inteiro curto. No entanto, não é um número inteiro curto. Este ano, é um número menor que 2009 e maior que -10000. No entanto, esse conjunto aumenta 1 por ano, à medida que o programa é executado. Tornar isso um "int curto" não é adequado. O que é necessário para tornar essa variável com segurança de tipo é uma função de validação em tempo de execução que garanta que o número seja sempre maior que -10000 e menor que no próximo ano civil.

Idiomas que usam digitação dinâmica (ou digitação de pato ou digitação de manifesto) como Perl, Python, Ruby, SQLite e Lua não possuem a noção de variáveis ​​digitadas. Isso força o programador a escrever uma rotina de validação em tempo de execução para cada variável para garantir que esteja correta ou suportar as conseqüências de exceções inexplicáveis ​​em tempo de execução. Na minha experiência, programadores em linguagens estaticamente tipadas como C, C ++, Java e C # costumam pensar que tipos definidos estaticamente é tudo o que precisam fazer para obter os benefícios da segurança de tipo. Isso simplesmente não é verdade para muitos programas de computador úteis e é difícil prever se é verdade para qualquer programa de computador específico.

O longo e o curto .... Deseja segurança de tipo? Nesse caso, escreva funções de tempo de execução para garantir que, quando uma variável recebe um valor, ela esteja em conformidade com critérios bem definidos. O lado negativo é que isso torna a análise de domínio realmente difícil para a maioria dos programas de computador, porque você precisa definir explicitamente os critérios para cada variável de programa.

Jay Godse
fonte
2
Variáveis ​​Python são digitadas ( fortemente tipadas, de fato). Tente fazer isso, por exemplo: "str" ​​+ 1. Você receberá um erro. No entanto, os tipos são verificados em tempo de execução, em vez de tempo de compilação.
Mipadi 4/11/2008