Co oznacza „bezpieczny typ”?
Co oznacza „bezpieczny typ”?
Odpowiedzi:
Bezpieczeństwo typu oznacza, że kompilator sprawdzi typy podczas kompilacji i zgłosi błąd, jeśli spróbujesz przypisać zły typ do zmiennej.
Kilka prostych przykładów:
// Fails, Trying to put an integer in a string
String one = 1;
// Also fails.
int foo = "bar";
Dotyczy to również argumentów metod, ponieważ przekazujesz im typy jawne:
int AddTwoNumbers(int a, int b)
{
return a + b;
}
Gdybym próbował to nazwać za pomocą:
int Sum = AddTwoNumbers(5, "5");
Kompilator zgłasza błąd, ponieważ przekazuję ciąg znaków („5”) i oczekuje on liczby całkowitej.
W luźno wpisanym języku, takim jak javascript, mogę wykonać następujące czynności:
function AddTwoNumbers(a, b)
{
return a + b;
}
jeśli nazywam to tak:
Sum = AddTwoNumbers(5, "5");
JavaScript automatycznie konwertuje 5 na ciąg znaków i zwraca „55”. Wynika to z javascript używającego znaku + do konkatenacji łańcucha. Aby było to zgodne z typem, musisz zrobić coś takiego:
function AddTwoNumbers(a, b)
{
return Number(a) + Number(b);
}
Lub ewentualnie:
function AddOnlyTwoNumbers(a, b)
{
if (isNaN(a) || isNaN(b))
return false;
return Number(a) + Number(b);
}
jeśli nazywam to tak:
Sum = AddTwoNumbers(5, " dogs");
JavaScript automatycznie konwertuje 5 na ciąg i dołącza je, aby zwrócić „5 psów”.
Nie wszystkie języki dynamiczne są tak wybaczające jak javascript (w rzeczywistości język dynamiczny nie implikuje luźnego języka pisanego (patrz Python)), niektóre z nich faktycznie powodują błąd w czasie wykonywania podczas rzutowania typu nieprawidłowego.
Chociaż jest to wygodne, otwiera cię na wiele błędów, które można łatwo przeoczyć i które można zidentyfikować tylko poprzez przetestowanie uruchomionego programu. Osobiście wolę, aby mój kompilator powiedział mi, czy popełniłem ten błąd.
Teraz wróć do C # ...
C # obsługuje funkcję języka o nazwie kowariancja , co w zasadzie oznacza, że można podstawić typ podstawowy na typ potomny i nie powodować błędów, na przykład:
public class Foo : Bar
{
}
Tutaj stworzyłem nową klasę (Foo), która podklasuje Bar. Teraz mogę utworzyć metodę:
void DoSomething(Bar myBar)
I nazwij to za pomocą Foo lub paska jako argumentu, oba będą działać bez powodowania błędu. Działa to, ponieważ C # wie, że każda klasa potomna Bar będzie implementować interfejs Bar.
Nie można jednak wykonać odwrotności:
void DoSomething(Foo myFoo)
W tej sytuacji nie mogę przekazać Bar do tej metody, ponieważ kompilator nie wie, że Bar implementuje interfejs Foo. Wynika to z faktu, że klasa potomna może (i zwykle będzie) znacznie różnić się od klasy macierzystej.
Oczywiście, teraz zszedłem daleko od głębokiego końca i wykroczyłem poza pierwotne pytanie, ale to wszystko dobrze wiedzieć :)
Bezpieczeństwo typów nie powinno być mylone z pisaniem statycznym / dynamicznym lub pisaniem mocnym / słabym.
Język bezpieczny dla typu to taki, w którym jedynymi operacjami, które można wykonać na danych, są te, które są akceptowane przez typ danych. Oznacza to, że jeśli dane są typu X
i X
nie obsługują operacji y
, język nie pozwoli na wykonanie y(X)
.
Ta definicja nie określa reguł, kiedy jest ona zaznaczona. Może być w czasie kompilacji (pisanie statyczne) lub w czasie wykonywania (pisanie dynamiczne), zwykle za pośrednictwem wyjątków. Może być trochę jedno i drugie: niektóre języki o typie statycznym pozwalają na rzutowanie danych z jednego typu na inny, a ważność rzutowań musi być sprawdzana w czasie wykonywania (wyobraź sobie, że próbujesz rzutować Object
na Consumer
- kompilator nie ma sposób na sprawdzenie, czy jest to dopuszczalne, czy nie).
Bezpieczeństwo tekstu niekoniecznie oznacza również silne pisanie na maszynie - niektóre języki są powszechnie słabo pisane, ale prawdopodobnie bezpieczne. Weźmy na przykład Javascript: jego system typów jest tak słaby, jak to tylko możliwe, ale wciąż ściśle określony. Umożliwia automatyczne rzutowanie danych (powiedzmy ciągów na int), ale w ramach ściśle określonych reguł. Według mojej wiedzy nie ma przypadku, w którym program JavaScript zachowałby się w nieokreślony sposób, a jeśli jesteś wystarczająco sprytny (nie jestem), powinieneś być w stanie przewidzieć, co się stanie podczas czytania kodu JavaScript.
Przykładem niebezpiecznego języka programowania jest C: odczyt / zapis wartości tablicy poza jej granicami ma niezdefiniowane zachowanie według specyfikacji . Nie da się przewidzieć, co się stanie. C jest językiem, który ma system typów, ale nie jest bezpieczny dla typów.
Bezpieczeństwo to nie tylko rodzaj kompilacji ograniczenia czasowe, ale czas pracy ograniczenia. Wydaje mi się, że nawet po tak długim czasie możemy to jeszcze wyjaśnić.
Istnieją 2 główne kwestie związane z bezpieczeństwem typu. Pamięć ** i typ danych (z odpowiednimi operacjami).
char
Typowo wymaga 1 bajt na znak lub 8 bitów (w zależności od języka, Java i C # przechowywania znaków Unicode, które wymagają 16 bitów). int
4 bajtów, lub 32 bitów (zazwyczaj).
Naocznie:
char: |-|-|-|-|-|-|-|-|
int : |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-|
Bezpieczny język typu nie pozwala na wstawienie int w char w czasie wykonywania (powinno to wyrzucić jakiś wyjątek klasy lub wyjątek braku pamięci). Jednak w niebezpiecznym języku typu nadpisujesz istniejące dane w kolejnych 3 sąsiednich bajtach pamięci.
int >> char:
|-|-|-|-|-|-|-|-| |?|?|?|?|?|?|?|?| |?|?|?|?|?|?|?|?| |?|?|?|?|?|?|?|?|
W powyższym przypadku 3 bajty po prawej stronie są nadpisywane, więc wszelkie wskaźniki tej pamięci (powiedzmy 3 kolejne znaki), które oczekują uzyskania przewidywalnej wartości char, będą teraz miały śmieci. Powoduje to undefined
zachowanie w twoim programie (lub gorzej, być może w innych programach, w zależności od sposobu przydzielania pamięci przez system operacyjny - w dzisiejszych czasach jest to bardzo mało prawdopodobne).
** Chociaż ten pierwszy problem nie dotyczy technicznego typu danych, wpisz bezpieczne języki, aby rozwiązać je nieodłącznie i wizualnie opisuje problem osobom nieświadomym, jak „alokacja pamięci” wygląda.
Bardziej subtelny i bezpośredni problem z typem występuje wtedy, gdy dwa typy danych używają tego samego przydziału pamięci. Weź int vs. niepodpisaną int. Oba mają 32 bity. (Równie łatwo może być char [4] i int, ale bardziej powszechnym problemem jest uint vs. int).
|-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-|
|-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-|
Niebezpieczny język typu pozwala programiście odwoływać się do prawidłowo przydzielonego zakresu 32 bitów, ale gdy wartość int bez znaku jest odczytywana w przestrzeni int (lub odwrotnie), znów mamy undefined
zachowanie. Wyobraź sobie problemy, jakie może to powodować w programie bankowym:
„Koleś! Przekroczyłem limit 30 $ i teraz mam 65 506 $ !!”
... ”, programy bankowe używają znacznie większych typów danych. ;) LOL!
Jak już zauważyli inni, kolejnym problemem są operacje obliczeniowe na typach. Zostało to już wystarczająco uwzględnione.
Większość programistów dzisiaj nigdy nie musi się martwić takimi rzeczami, chyba że używa czegoś takiego jak C lub C ++. Oba te języki pozwalają programistom łatwo naruszać bezpieczeństwo typu w czasie wykonywania (bezpośrednie odwoływanie do pamięci), pomimo najlepszych starań kompilatorów, aby zminimalizować ryzyko. JEDNAK to nie wszystko jest złe.
Jednym z powodów, dla których języki te są tak szybkie obliczeniowo, jest to, że nie są obciążone weryfikacją zgodności typów podczas operacji w czasie wykonywania, takich jak na przykład Java. Zakładają, że programista jest dobrym racjonalnym bytem, który nie doda łańcucha i int razem, a za to programista jest nagradzany szybkością / wydajnością.
Wiele odpowiedzi tutaj łączy bezpieczeństwo typu z typowaniem statycznym i dynamicznym. Język dynamicznie typowany (jak smalltalk) może być również bezpieczny dla typu.
Krótka odpowiedź: język jest uznawany za bezpieczny dla typu, jeśli żadna operacja nie prowadzi do nieokreślonego zachowania. Wielu uważa wymóg jawnego typowania konwersji za niezbędny do ścisłego wpisania języka , ponieważ automatyczne konwersje mogą czasem prowadzić do dobrze określonych, ale nieoczekiwanych / nieintuicyjnych zachowań.
if no operation leads to undefined behavior
.
Wyjaśnienie z wydziału sztuk wyzwolonych, a nie z wydziału nauk ścisłych:
Kiedy ludzie mówią, że język lub funkcja języka jest bezpieczna dla typu, oznacza to, że język pomoże ci na przykład przekazać coś, co nie jest liczbą całkowitą, do logiki, która oczekuje liczby całkowitej.
Na przykład w C # definiuję funkcję jako:
void foo(int arg)
Kompilator powstrzyma mnie od zrobienia tego:
// call foo
foo("hello world")
W innych językach kompilator mnie nie zatrzyma (lub nie ma kompilatora ...), więc ciąg znaków zostanie przekazany do logiki i prawdopodobnie nastąpi coś złego.
Wpisz bezpieczne języki, spróbuj złapać więcej w „czasie kompilacji”.
Z drugiej strony, z bezpiecznymi językami typu, gdy masz ciąg taki jak „123” i chcesz operować nim jak int, musisz napisać więcej kodu, aby przekonwertować ciąg na int lub gdy masz int jak 123 i chcesz użyć go w komunikacie typu „Odpowiedź to 123”, musisz napisać więcej kodu, aby przekonwertować / rzutować na ciąg.
Aby lepiej zrozumieć, obejrzyj poniższy film, który pokazuje kod w bezpiecznym języku (C #), a NIE w bezpiecznym języku (javascript).
http://www.youtube.com/watch?v=Rlw_njQhkxw
Teraz długi tekst.
Bezpieczeństwo typu oznacza zapobieganie błędom typu. Błąd typu występuje, gdy typ danych jednego typu jest przypisywany do innego typu NIEZNACZNIE i otrzymujemy niepożądane wyniki.
Na przykład JavaScript nie jest bezpiecznym językiem typu. W poniższym kodzie „num” jest zmienną numeryczną, a „str” jest łańcuchem. Javascript pozwala mi wykonywać „num + str”, teraz GUESS zrobi arytmetykę lub konkatenację.
Teraz dla poniższego kodu wyniki wynoszą „55”, ale ważnym punktem jest zamieszanie, które stworzyło, jaką operację wykona.
Dzieje się tak, ponieważ javascript nie jest bezpiecznym językiem. Pozwala to ustawić jeden typ danych na inny typ bez ograniczeń.
<script>
var num = 5; // numeric
var str = "5"; // string
var z = num + str; // arthimetic or concat ????
alert(z); // displays “55”
</script>
C # jest językiem bezpiecznym dla typu. Nie pozwala na przypisanie jednego typu danych do innego typu danych. Poniższy kod nie pozwala operatorowi „+” na różnych typach danych.
Bezpieczny typ oznacza, że programowo typ danych dla zmiennej, wartości zwracanej lub argumentu musi spełniać określone kryteria.
W praktyce oznacza to, że 7 (typ liczby całkowitej) różni się od „7” (cytowany znak typu string).
PHP, JavaScript i inne dynamiczne języki skryptowe są zwykle słabo wpisane, ponieważ konwertują (ciąg) „7” na (liczbę całkowitą) 7, jeśli spróbujesz dodać „7” + 3, chociaż czasami musisz to zrobić jawnie (a Javascript używa znaku „+” do konkatenacji).
C / C ++ / Java tego nie zrozumie lub zamiast tego połączy wynik w „73”. Bezpieczeństwo typu zapobiega tego rodzaju błędom w kodzie, wyraźnie określając wymagania dotyczące typu.
Bezpieczeństwo typu jest bardzo przydatne. Rozwiązaniem powyższego „7” + 3 byłoby typ cast (int) „7” + 3 (równa się 10).
Pojęcie:
Aby być bardzo prostym Typ Bezpieczny jak znaczeń, upewnia się, że typ zmiennej powinien być bezpieczny
więc chodzi przede wszystkim o bezpieczeństwo typów pamięci w zakresie zmiennych.
Spróbuj tego wyjaśnienia na ...
TypeSafe oznacza, że zmienne są sprawdzane statycznie pod kątem odpowiedniego przypisania w czasie kompilacji. Na przykład podaj łańcuch lub liczbę całkowitą. Te dwa różne typy danych nie mogą być przypisywane krzyżowo (tzn. Nie można przypisać liczby całkowitej do ciągu, ani nie można przypisać ciągu do liczby całkowitej).
W przypadku zachowania innego niż bezpieczny, rozważ to:
object x = 89;
int y;
jeśli spróbujesz to zrobić:
y = x;
kompilator zgłasza błąd, który mówi, że nie może przekonwertować System.Object na liczbę całkowitą. Musisz to zrobić jawnie. Jednym ze sposobów byłoby:
y = Convert.ToInt32( x );
Powyższe przypisanie nie jest bezpieczne. Przypisanie bezpiecznego typu to miejsce, w którym typy mogą być bezpośrednio przypisane do siebie.
W programie ASP.NET znajdują się kolekcje inne niż bezpieczne (np. Kolekcje aplikacji, sesji i stanu wyświetlania). Dobrą wiadomością o tych kolekcjach jest to, że (minimalizując kwestie związane z zarządzaniem wieloma serwerami) można umieścić praktycznie dowolny typ danych w dowolnym z trzech zbiorów. Zła wiadomość: ponieważ te kolekcje nie są bezpieczne dla typów, musisz odpowiednio rzucić wartości, gdy je odzyskasz.
Na przykład:
Session[ "x" ] = 34;
działa w porządku. Ale aby ponownie przypisać wartość całkowitą, musisz:
int i = Convert.ToInt32( Session[ "x" ] );
Przeczytaj o rodzajach, aby dowiedzieć się, w jaki sposób funkcja ta ułatwia wdrażanie bezpiecznych kolekcji.
C # to język bezpieczny dla typów, ale uważaj na artykuły o C # 4.0; pojawiają się ciekawe dynamiczne możliwości (czy to dobrze, że C # zasadniczo dostaje Option Strict: Off ... zobaczymy).
Type-Safe to kod, który uzyskuje dostęp tylko do miejsc w pamięci, do których ma dostęp, i tylko w dobrze określony, dozwolony sposób. Kod bezpieczny dla typu nie może wykonać operacji na obiekcie, który jest nieprawidłowy dla tego obiektu. Kompilatory języka C # i VB.NET zawsze generują kod typu, który jest sprawdzany pod kątem bezpieczeństwa typu podczas kompilacji JIT.
Bezpieczeństwo typu oznacza, że zestaw wartości, które można przypisać do zmiennej programu, musi spełniać dobrze zdefiniowane i testowalne kryteria. Zmienne bezpieczne dla typu prowadzą do bardziej niezawodnych programów, ponieważ algorytmy manipulujące zmiennymi mogą ufać, że zmienna pobierze tylko jeden z dobrze zdefiniowanego zestawu wartości. Utrzymanie tego zaufania zapewnia integralność i jakość danych i programu.
W przypadku wielu zmiennych zestaw wartości, które można przypisać do zmiennej, jest definiowany w momencie pisania programu. Na przykład zmienna o nazwie „kolor” może przyjmować wartości „czerwony”, „zielony” lub „niebieski” i nigdy żadnych innych wartości. W przypadku innych zmiennych kryteria te mogą ulec zmianie w czasie wykonywania. Na przykład zmienna o nazwie „kolor” może przyjmować wartości tylko w kolumnie „nazwa” tabeli „Kolory” w relacyjnej bazie danych, gdzie „czerwony”, „zielony” i „niebieski” to trzy wartości dla „nazwa” w tabeli „Kolory”, ale jakaś inna część programu komputerowego może być w stanie dodać do tej listy podczas działania programu, a zmienna może przyjmować nowe wartości po ich dodaniu do tabeli Kolory .
Wiele języków bezpiecznych dla typu daje złudzenie „bezpieczeństwa typu”, kładąc nacisk na ścisłe definiowanie typów dla zmiennych i pozwalając, aby zmiennej przypisywano tylko wartości tego samego „typu”. Z tym podejściem wiąże się kilka problemów. Na przykład program może mieć zmienną „yearOfBirth”, czyli rok, w którym dana osoba się urodziła, i kusi go, aby wpisać go jako krótką liczbę całkowitą. Nie jest to jednak liczba całkowita krótka. W tym roku jest to liczba mniejsza niż 2009 i większa niż -10000. Jednak ten zestaw rośnie o 1 każdego roku w miarę działania programu. Uczynienie tego „krótkim int” nie jest wystarczające. Aby ta zmienna była bezpieczna dla typu, potrzebna jest funkcja sprawdzania poprawności w czasie wykonywania, która zapewnia, że liczba jest zawsze większa niż -10000 i mniejsza niż następny rok kalendarzowy.
Języki używające dynamicznego pisania (lub pisania kaczego lub manifestowania), takie jak Perl, Python, Ruby, SQLite i Lua, nie mają pojęcia zmiennych pisanych. Zmusza to programistę do napisania procedury sprawdzania poprawności w czasie wykonywania dla każdej zmiennej, aby upewnić się, że jest poprawna, lub znosić konsekwencje niewyjaśnionych wyjątków w czasie wykonywania. Z mojego doświadczenia wynika, że programiści posługujący się statycznie typowanymi językami, takimi jak C, C ++, Java i C #, często są uśpieni myśleniem, że statycznie zdefiniowane typy są wszystkim, co muszą zrobić, aby uzyskać korzyści z bezpieczeństwa typu. Nie jest to po prostu prawdą w przypadku wielu przydatnych programów komputerowych i trudno jest przewidzieć, czy jest to prawdą w odniesieniu do jakiegokolwiek konkretnego programu komputerowego.
Długa i krótka .... Czy chcesz mieć bezpieczeństwo typu? Jeśli tak, to napisz funkcje czasu wykonywania, aby upewnić się, że zmiennej przypisuje się wartość, która jest zgodna z dobrze zdefiniowanymi kryteriami. Wadą jest to, że sprawia, że analiza domen jest naprawdę trudna dla większości programów komputerowych, ponieważ musisz jawnie zdefiniować kryteria dla każdej zmiennej programu.