2013-05-07 32 views
33

C#では暗黙的にlongintに変換することはできません。C#の型変換に矛盾がありますか?

long l = 5; 
int i = l; // CS0266: Cannot implicitly convert type 'long' to 'int'. An explicit conversion exists (are you missing a cast?) 

このエラーが発生します。そしてそうだ。私がそうするなら、私は間違った切り捨てのために私のデータを破る危険にさらされています。私が何をしているのか分かったら、いつでも明示的にキャストし、コンパイラに切り捨てても大丈夫だと私には分かります。

int i = (int)l; // OK 

しかし、同じメカニズムがforeachループを使用したときに適用していないようです。

IList<long> myList = new List<long>(); 
foreach (int i in myList) 
{ 
} 

コンパイラも、それは本質的には同じものであっても、ここに警告を生成しません:intlongのチェックを外す切り捨て、非常によく私のデータを壊すかもしれません。

私の質問は単純です:なぜこれはforeachが変数割り当てと同じエラーを作成しないのですか?

+0

私は、コンパイラがイテレータコードを生成するときに明示的なキャストとして 'int'の指定を扱うと信じています。 'foreach(grid.Rows内のDataGridViewRow行)が'オブジェクト 'の型を生成するときに' foreach(var row in grid.Rows) 'が' DataGridView'行コレクションで同じことを時折経験します。 –

+0

[キャストと型変換](http://msdn.microsoft.com/en-us/library/ms173105.aspx)、['.NET Frameworkでの型変換](http://msdn.microsoft .com/en-us/library/98bbex99.aspx) –

+0

私は 'foreach int'を実行しているのでエラーを作成しないと思います。' long'は整数型です。 – MyCodeSucks

答えて

30

更新日:この質問はthe subject of my blog in July of 2013でした。素晴らしい質問をありがとう!

なぜこの割り当ては変数割り当てと同じエラーを作成しませんか?

「なぜ」という質問は、あなたが求めている「本当の」質問が分からないために答えにくいです。その質問に答える代わりに、私はいくつかの異なる質問に答えます。

この仕様のどの部分がこの動作を正当化していますか?

マイケル・リューの答えが正確に指摘しているように、それはセクション8.8.4です。

明示変換の全体のポイントは、変換コードで明示なければならないことです。それで私たちはキャスト演算子を持っています。それは "ここに明示的な変換がある"という大きな旗を振っています。これは明示的な変換がコード内に存在しないC#では数回のうちの1つです。デザインチームが目に見えない形で「明示的」な変換を挿入する要因は何でしたか?

foreachループはジェネリックの前に設計されました。

あなたが言っているしたくない
ArrayList myList = new ArrayList(); 
myList.Add("abc"); 
myList.Add("def"); 
myList.Add("ghi"); 

:あなたはほとんど常にあなたが先リストにあるどのような種類の時間の知っているに持っているジェネリック医薬品のない世界で

foreach(object item in myList) 
{ 
    string current = (string)item; 

、およびその知識がある。しかし、この情報は型システムでは捕捉されません。そのため、あなたは何とかコンパイラに指示する必要があり、あなたは

foreach(string item in myList) 

を言うことによってこのリストはちょうどキャストのように、文字列の完全であるコンパイラへのあなたの主張は、特定の項目があるという主張がされていることをやります文字列。

ジェネリックの世界では、これが間違っているということは間違いありません。それを今すぐ変えることになるので、私たちはそれに固執しています。

この機能はかなり混乱しています。それは本当にそれぞれのため」であるとき、「次の操作を行い、このリスト内の文字列型の各オブジェクトについて」、である

while(enumerator.MoveNext()) 
{ 
    if (!(enumerator.Current is string) continue; 
    string item = (string)enumerator.Current; 

:私は最初のプログラミングのC#を始めたとき、私はそれが何かの意味を持っていたと仮定しましたこのリスト内のオブジェクトは、その項目が文字列であると主張し、次のことを行います... "(もし前者が実際に望むものであれば、OfType<T>()拡張メソッドを使用してください)。

言語の終わりバージョン2のタイプシステムを大規模に変更すると、奇妙な "従来の"機能が使用されます。

ジェネリックが使用されている現代のコードでは、コンパイラはこのケースについて警告を出すべきですか?

私はそれを考慮しました。我々の研究は、

foreach(Giraffe in listOfMammals) 

がとても共通であることが示されたこと、我々は正しいコードに対して警告を与えることだろうほとんどの時間を。それは "エラーとしての警告"をオンにしてコンパイルするすべての人に問題を引き起こします。そして、一般的にコードに警告を付けるのは悪いと言われますが、は実際にはです。私たちは警告を守らないことに決めました。

C#コンパイラが明示的に変換を不可視に挿入する状況はありますか?

はい。実際に誰かがこの1の後にそのわずか数時間について質問を:

Compiler replaces explicit cast to my own type with explicit cast to .NET type?

明示的な変換が同様に挿入されているいくつかの非常にあいまいな相互運用シナリオがあります。

+0

C#1.0では、InvalidCas 'int'変数で' long'値の 'ArrayList'を列挙すると、' tException'が発生し、C#4.0では、 'int i in myList select i'からLINQクエリを列挙すると' InvalidCastException'が発生します。基本的には、「long」から「int」への自動的な数値変換から、驚くべきことだと思います。私は、明示的な参照とアンボクシング変換のみが実行されることを期待しています。 –

+1

@Michaelは非ジェネリックなarraylistを使って、ボックス化されたlongを得るには '(int)(long)value'が必要なので、foreachの変換セマンティクスは失敗します。 T(long)を公開する汎用の列挙子を使用する場合、intは簡単な変換です。 –

+0

コレクションは強く型付けされていても(通常はSharePoint関連のもので作業するとき)、foreach(var ...)は 'object'を返すことが多いので、' var'をたくさん使う傾向があります。私は 'foreach'を使うときに明示的なキャストをしていると受け入れています:) – Shaamaan

5

簡単な答えはforeachです。背後に明示的なキャストはありません。別の例:

public class Parent { } 
    public class Child : Parent { } 

    IList<Parent> parents = new List<Parent>() 
    { 
     new Parent() 
    }; 
    foreach (Child child in parents) { } 

また、これはコンパイルエラーが発生しませんが、実行時にInvalidCastExceptionをスローします。

13

C#4.0仕様の§8.8.4に定義されているように、フォーム

foreach (V v in x) embedded-statement 

foreach文が

C "は、コレクション型" と T
{ 
    E e = ((C)(x)).GetEnumerator(); 
    try { 
     V v; 
     while (e.MoveNext()) { 
      v = (V)(T)e.Current; // <-- note the explicit cast to V 
      embedded-statement 
     } 
    } 
    finally { 
     … // Dispose e 
    } 
} 

に展開されxから推測される「要素タイプ」です。

V(あなたの場合はint)へのキャストは、あなたの例をコンパイルできるものです。

Vへのキャスト理由:C#1.0でジェネリックが追加される前に、通常、ArrayListのようなコレクションを列挙するときに明示的なキャストが必要でした。なぜなら、コンパイラは型コレクション内の値の

関連する問題