タイプセーフとは何ですか?


回答:


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
 {
 }

ここでは、Barをサブクラス化する新しいクラス(Foo)を作成しました。これでメソッドを作成できます:

 void DoSomething(Bar myBar)

FooまたはBarを引数として使用して呼び出すと、どちらもエラーを発生させることなく機能します。これは、C#がBarのすべての子クラスがBarのインターフェイスを実装することを知っているため機能します。

ただし、その逆はできません。

void DoSomething(Foo myFoo)

この状況では、BarがFooのインターフェースを実装していることをコンパイラーが認識していないため、Barをこのメソッドに渡すことができません。これは、子クラスが親クラスと大きく異なる可能性があるためです(通常はそうなります)。

もちろん、今は元の質問の範囲を超えて、深いところから抜け出しましたが、知っておくと便利なことです:)


26
この答えは間違っていると思います。型の安全性は必ずしもコンパイル時に強制されるわけではありません。たとえば、Schemeはタイプセーフと見なされますが、動的にチェックされます(実行時にタイプセーフが適用されます)。これは主に、Benjamin C. Pierceによる型とプログラミング言語の紹介を言い換えたものです。
ニコラリノード2014

11
あなたが説明するものは、共分散ではなく、ポリモーフィズムと呼ばれます。ジェネリックでは共分散が使用されます。
IllidanS4はモニカに2015

@NicolasRinaudoは、動的言語と静的言語の間のギャップが、「解釈された」言語の動的コンパイルとプリコンパイル、および「コンパイルされた」言語でのリフレクションによって侵食されていることに注意してください。たとえば、リフレクションを使用すると、実行時のダックタイピングが可能になるため、コンパイルされた言語で「これにはQuack()メソッドがあります。これを呼び出して、何が起こるか確認します」と言うことができます。Pascalに似た言語では、多くの場合(オプションで)ランタイムオーバーフローチェックが行われるため、実行時に「コンパイラー」エラーが発生し、「8ビットの宛先{コアダンプ}に整数を入力できません」が発生します。
2017年

2
あなたの例は、タイプセーフティと同じではない「強く型付けされた」と呼ばれる概念を参照しています。型安全性とは、言語が実行時またはコンパイル時に型エラーを検出できる場合です。たとえば、Pythonは弱い型付けと型安全です。この回答は誤解を招く可能性があるため、フラグを付ける必要があります。
ダンテバルバ

一般的な説明と
比較して

57

型安全性は、静的/動的型付けまたは厳密/弱い型付けと混同しないでください。

タイプセーフ言語とは、データに対して実行できる唯一の操作が、データの型によって容認される操作である言語です。つまり、データがタイプでXあり、X操作をサポートしていないy場合、言語は実行を許可しませんy(X)

この定義はこれがいつチェックされるかについてのルールを設定しません。コンパイル時(静的型付け)または実行時(動的型付け)に、通常は例外を介して行われます。これは、両方のビットであることができますいくつかの静的型付け言語は一つのタイプから別のキャストデータに、あなたにできるように、そしてキャストの妥当性は、実行時にチェックする必要があります(あなたがキャストしようとしていることを想像ObjectするConsumer-コンパイラはありません持っていますそれが許容できるかどうかを知る方法)。

型安全性は必ずしも強く型付けされていることを意味するわけではありません-一部の言語は悪名高い弱い型付けで有名ですが、間違いなく型安全です。たとえば、JavaScriptを例にとります。その型システムは、登場するのと同じくらい弱いものの、厳密に定義されています。データ(たとえば、文字列をintに)を自動的にキャストできますが、明確に定義されたルールの範囲内です。私の知る限りでは、JavaScriptプログラムが未定義の方法で動作するケースはありません。十分に賢い場合(私はそうではありません)、JavaScriptコードを読み取るときに何が起こるかを予測できるはずです。

型に安全でないプログラミング言語の例はCです。配列の境界外で配列値を読み書きすると、仕様により未定義の動作になります。何が起こるかを予測することは不可能です。Cは型システムを持つ言語ですが、タイプセーフではありません。


1
型安全でない言語の他の例は何ですか?「配列の境界外に配列値を書き込むと、仕様によっては未定義の動作があります。何が起こるかを予測することは不可能です」とはどういう意味ですか。JavaScriptのように、それは未定義の権利を返しますか?または本当に何が起こる可能性があります。この例を挙げていただけますか?
ARK

1
@AkshayrajKore確かに。配列はメモリポインタであるため、境界を超えて書き込むと、別のプログラムのデータを上書きする可能性があります-何も実行できず、プログラムがクラッシュし、ハードドライブが消去されます-未定義であり、メモリのそのビットを誰がどのように読み取っているかに依存しますそれはそれに反応します。
Nicolas Rinaudo

@Nicolas Rinaudo不正解です。仮想メモリについて読む必要があります。各プロセスには独自の仮想アドレス空間があるため、プロセスはこのように「別のプログラムのデータを上書き」できません。
ilstam

あなたは正しいです-これはあなたがあなたのプログラムのメモリの別の部分を上書きしている可能性があることを読んでいるはずです- 私は信じていますが、それ自体はプログラム自体ですか?
Nicolas Rinaudo 2018

@NicolasRinaudoプログラムのコードセグメントは、仮想アドレス空間で読み取り専用にマップされます。したがって、そこに書き込もうとすると、セグメンテーション違反が発生し、プログラムがクラッシュします。同様に、マップされていないメモリに書き込もうとすると、ページ違反が発生し、再びクラッシュします。ただし、運が悪ければ、プロセスのスタックまたはヒープ(他の変数や他のものなど)からデータを上書きするだけかもしれません。その場合、おそらくすぐにはクラッシュしないでしょうが、それはさらに悪いことに(後でうまくいけば)後でないとバグに気付かないためです!
ilstam 2018

32

タイプセーフは、コンパイル時の制約だけでなく、実行時の制約でもあります。結局のところ、私はこれをさらに明確にすることができると感じています。

タイプセーフティに関連する2つの主な問題があります。メモリ**とデータタイプ(対応する操作を含む)。

メモリ**

A charは通常、1文字あたり1バイトまたは8ビットを必要とします(言語、JavaおよびC#ストアのUnicode文字は16ビットを必要とします)。にintは4バイトまたは32ビット(通常)が必要です。

視覚的に:

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

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

タイプセーフ言語では、実行時に intをcharに挿入できません(これにより、ある種のクラスキャストまたはメモリ不足の例外がスローされます)。ただし、タイプが安全でない言語では、隣接する3バイト以上のメモリにある既存のデータを上書きします。

int >> char:

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

上記の場合、右側の3バイトが上書きされるため、予測可能なchar値を取得することを期待しているそのメモリへのポインター(たとえば、3つの連続したchar)はガベージになります。これによりundefined、プログラムで動作が発生します(あるいは、OSがメモリを割り当てる方法によっては、おそらく他のプログラムでも動作する可能性があります-最近はほとんどありません)。

** この最初の問題は技術的にはデータ型に関するものではありませんが、タイプセーフ言語は本質的に問題に対処し、メモリ割り当てがどのように「見える」かを知らない人に問題を視覚的に説明します。

データ・タイプ

より微妙で直接的な型の問題は、2つのデータ型が同じメモリ割り当てを使用する場合です。intとunsigned intを比較します。どちらも32ビットです。(char [4]とintのように簡単にできますが、より一般的な問題はuintとintです)。

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

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

型安全でない言語を使用すると、プログラマは32ビットの適切に割り当てられたスパンを参照できますが、unsigned intの値がintのスペースに読み込まれると(またはその逆)、再びundefined動作します。これが銀行プログラムで発生する可能性のある問題を想像してみてください。

「おい!私は$ 30の当座貸越をしました、そして今私は$ 65,506を残しています!!」

... 'コース、銀行プログラムははるかに大きなデータ型を使用します。;) 笑!

他の人がすでに指摘したように、次の問題は型の計算操作です。それはすでに十分にカバーされています。

速度と安全性

今日のほとんどのプログラマーは、CやC ++のようなものを使用していない限り、そのようなことを心配する必要はありません。これらの言語の両方により、プログラマーは、リスクを最小限に抑えるためのコンパイラーの最善の努力にもかかわらず、実行時に型の安全性(直接メモリ参照)に簡単に違反することができます。しかし、これは必ずしも悪いことではありません。

これらの言語が計算速度が非常に速い理由の1つは、Javaなどの実行時の操作中に型の互換性を確認する必要がないためです。彼らは、開発者が文字列とintを一緒に追加しない優れた合理的な存在であると想定しています。そのため、開発者は速度/効率で報酬を受けます。


27

ここでの多くの答えは、型安全性を静的型付けおよび動的型付けと混同します。動的に型付けされた言語(smalltalkなど)も、タイプセーフにすることができます。

短い答え:操作が未定義の動作を引き起こさない場合、言語はタイプセーフと見なされます。自動変換は明確に定義されているが予期しない/直感的でない動作につながることがあるので、言語を厳密に型指定するために必要な明示的な型変換の要件を考慮する人は少なくありません。


1
ちょっと待って、タイプセーフの定義に "type":Dという単語はありません if no operation leads to undefined behavior
VasiliNovikov 14

1
また、そのような定義には同意しません。タイプセーフとは、正確には1.タイプの存在2.コンパイラーに対するタイプの知識、そしてもちろん適切なチェックを意味すると思います。
VasiliNovikov 14

10

「タイプセーフ」なプログラミング言語とは、次のことを意味します。

  1. 初期化されていない変数から読み取ることはできません
  2. 境界を超えて配列にインデックスを付けることはできません
  3. チェックされていない型キャストは実行できません

8

リベラルアーツ専攻からの説明であり、コンプサイエンス専攻ではありません:

言語または言語機能がタイプセーフであると人々が言うとき、その言語は、たとえば整数でないものを整数を期待するロジックに渡すことを防ぐのに役立つことを意味します。

たとえば、C#では、関数を次のように定義します。

 void foo(int arg)

コンパイラーは私がこれをするのを止めます:

  // call foo
  foo("hello world")

他の言語では、コンパイラーは私を止めません(またはコンパイラーがありません...)。そのため、ストリングはロジックに渡され、おそらく何か悪いことが起こります。

タイプセーフ言語は、「コンパイル時」により多くをキャッチしようとします。

欠点として、タイプセーフ言語では、 "123"のような文字列があり、それをintのように操作したい場合、文字列をintに変換するためのコードを追加するか、intがある場合123のようで、「答えは123です」のようなメッセージでそれを使用したい場合は、それを文字列に変換/キャストするためのコードをさらに記述する必要があります。


4
リベラルアーツは主要言う説明を:)あなたはまた、静的型付けと動的型付けをconflatingしています。
ididak 2008年

1
リベラルアーツは「専攻」ではなく「専攻」です。
Corey Trager

5

理解を深めるには、タイプセーフ言語(C#)ではなくタイプセーフ言語(javascript)のコードを示す以下のビデオをご覧ください。

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

長いテキストです。

タイプセーフとは、タイプエラーを防ぐことです。型エラーは、ある型のデータ型が他の型にUNKNOWINGLYに割り当てられている場合に発生し、望ましくない結果が得られます。

たとえば、JavaScriptはタイプセーフな言語ではありません。以下のコードでは、「num」は数値変数で、「str」は文字列です。Javascriptを使用すると、「num + str」を実行できます。GUESSは、算術演算または連結を実行します。

以下のコードの結果は「55」ですが、重要な点は、どのような操作を行うかによって生じる混乱です。

これは、JavaScriptがタイプセーフ言語ではないために発生しています。1つのタイプのデータを他のタイプに制限なしに設定することができます。

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

C#はタイプセーフ言語です。1つのデータ型を他のデータ型に割り当てることはできません。以下のコードでは、さまざまなデータ型で「+」演算子を使用できません。

ここに画像の説明を入力してください


4

タイプセーフとは、プログラムによって、変数、戻り値、または引数のデータ型が特定の基準内に収まる必要があることを意味します。

実際には、これは7(整数型)が "7"(文字列型の引用文字)とは異なることを意味します。

PHP、Javascript、およびその他の動的スクリプト言語は通常、弱く型付けされており、 "7" + 3を追加しようとすると、(文字列) "7"が(整数)7に変換されます。明示的に(そしてJavaScriptは連結に「+」文字を使用します)。

C / C ++ / Javaはそれを理解しないか、代わりに結果を "73"に連結します。型保証は、型の要件を明示的にすることで、コード内のこれらのタイプのバグを防ぎます。

タイプセーフは非常に便利です。上記の "7" + 3の解決策は、型キャスト(int) "7" + 3(10と等しい)です。


3

概念:

意味のように非常に単純なタイプセーフであるためには、変数の型が次のように安全であることを確認します

  1. 間違ったデータ型はありません。たとえば、文字列型の変数を整数で保存または初期化できません。
  2. 範囲外のインデックスにはアクセスできません
  3. 特定のメモリ位置のみを許可する

したがって、変数の観点から見たストレージのタイプの安全性がすべてです。


2

この説明を試してください...

TypeSafeは、コンパイル時に変数が適切に割り当てられているか静的にチェックされることを意味します。たとえば、文字列または整数を検討します。これら2つの異なるデータ型を相互に割り当てることはできません(つまり、整数を文字列に割り当てることも、文字列を整数に割り当てることもできません)。

タイプセーフでない動作については、これを考慮してください:

object x = 89;
int y;

これを行おうとすると:

y = x;

コンパイラは、System.ObjectをIntegerに変換できないことを示すエラーをスローします。明示的に行う必要があります。1つの方法は次のとおりです。

y = Convert.ToInt32( x );

上記の割り当てはタイプセーフではありません。タイプセーフな割り当てでは、タイプを互いに直接割り当てることができます。

タイプセーフではないコレクションがASP.NETに豊富にあります(たとえば、アプリケーション、セッション、およびビューステートコレクション)。これらのコレクションの良い点は、(複数のサーバー状態管理の考慮事項を最小限に抑えて)3つのコレクションのいずれにもほとんどすべてのデータ型を配置できることです。悪いニュース:これらのコレクションはタイプセーフではないため、値をフェッチして戻すときに、値を適切にキャストする必要があります。

例えば:

Session[ "x" ] = 34;

正常に動作します。ただし、整数値を割り当てるには、次のことを行う必要があります。

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

タイプセーフなコレクションを簡単に実装するのに役立つ機能のジェネリックについて読んでください。

C#はタイプセーフ言語ですが、C#4.0に関する記事に注意してください。興味深い動的な可能性が迫っています(C#が基本的にOption Strict:Off ...を取得しているのは良いことです)。


個人的には、Convert.To表記が嫌いです。セーフキャストを使用しないのはなぜですか。呼び出しスタックでの関数呼び出しも少なくなっています。
FlySwat 2008年

2

タイプセーフは、アクセスが許可されているメモリ位置にのみアクセスするコードであり、明確に定義された、許可された方法でのみアクセスします。タイプセーフコードは、オブジェクトに対して無効な操作を実行できません。C#およびVB.NET言語コンパイラは、常にタイプセーフコードを生成します。これは、JITコンパイル中にタイプセーフであることが確認されています。


メモリの安全性ですか?
ゴロポット2017年

1

タイプセーフとは、プログラム変数に割り当てることができる値のセットが、明確に定義されたテスト可能な基準に適合している必要があることを意味します。変数を操作するアルゴリズムは、変数が明確に定義された値のセットの1つだけを取ると信頼できるため、タイプセーフな変数はより堅牢なプログラムにつながります。この信頼を維持することで、データとプログラムの整合性と品質が保証されます。

多くの変数の場合、変数に割り当てることができる値のセットは、プログラムの作成時に定義されます。たとえば、「色」と呼ばれる変数は、「赤」、「緑」、または「青」の値をとることが許可され、その他の値をとることはできません。他の変数の場合、これらの基準は実行時に変更される場合があります。たとえば、「color」という変数は、リレーショナルデータベースの「Colours」テーブルの「name」列の値のみを取ることができます。「red」、「green」、「blue」は3つの値です。 「色」テーブルの「名前」の場合、コンピュータプログラムの他の一部は、プログラムの実行中にそのリストに追加できる場合があり、変数は、色テーブルに追加された後、新しい値を取ることができます。 。

多くのタイプセーフ言語は、変数のタイプを厳密に定義し、変数に同じ「タイプ」の値のみを割り当てることを許可することにより、「タイプセーフ」のような錯覚を与えます。このアプローチにはいくつかの問題があります。たとえば、プログラムには変数「yearOfBirth」があり、これは人が生まれた年であり、短い整数として型キャストしたくなります。ただし、短整数ではありません。今年は、2009年未満で-10000を超える数値です。ただし、プログラムが実行されると、このセットは毎年1つずつ増加します。これを「short int」にすることは適切ではありません。この変数をタイプセーフにするために必要なのは、数値が常に-10000より大きく、翌暦年よりも小さいことを確認する実行時検証関数です。

Perl、Python、Ruby、SQLite、Luaなどの動的型付け(またはダック型付け、マニフェスト型付け)を使用する言語には、型付き変数の概念はありません。これにより、プログラマーはすべての変数のランタイム検証ルーチンを作成して、それが正しいことを確認するか、原因不明のランタイム例外の影響に耐えることができます。私の経験では、C、C ++、Java、C#などの静的型付き言語のプログラマーは、静的に定義された型が型安全の利点を得るために必要なすべてのことであると考えがちです。これは、多くの有用なコンピュータープログラムには当てはまらず、特定のコンピュータープログラムに当てはまるかどうかを予測することは困難です。

ロング&ショート....タイプセーフが必要ですか?その場合は、ランタイム関数を記述して、変数に値が割り当てられたときに、明確に定義された基準に確実に準拠するようにします。欠点は、各プログラム変数の基準を明示的に定義する必要があるため、ほとんどのコンピュータープログラムでドメイン分析が非常に困難になることです。


2
Python変数は型付けされています(実際には強く型付けされています)。たとえば、 "str" + 1のようにしてみてください。エラーが発生します。ただし、型はコンパイル時ではなく実行時にチェックされます。
mipadi 2008年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.