nullチェックがない場合でも、キャストの代わりに「as」を使用することには意味がありますか?[閉まっている]


351

開発ブログ、オンラインコードの例、そして(最近では)本でも、次のようなコードについてつまずきます。

var y = x as T;
y.SomeMethod();

さらに悪いことに:

(x as T).SomeMethod();

それは私には意味がありません。xタイプがであることが確実な場合はT、ダイレクトキャストを使用する必要があります(T)x。不明な場合は使用できますasnull、操作を実行する前に確認する必要があります。上記のコードが行うことは、(有用な)InvalidCastExceptionを(役に立たない)に変えることだけですNullReferenceException

これがasキーワードの露骨な乱用だと思うのは私だけですか?それとも私は明白なものを見逃していて、上記のパターンが実際に理にかなっていますか?


56
見た目がおかしい(Sとしてキス)。SteveIsSuchA(); しかし、私は同意します、それは虐待です。
SwDevMan81 2010年

5
書くよりも格段にカッコ((T)x).SomeMethod()いいですよね。;)(冗談です、あなたはもちろん正しいです!)
Lucero

8
@P Daddy同意しない、完全に良い質問(このコードパターンは実際に意味があるか)、そして非常に役立つ。質問に+1し、締めくくる投票者には眉をひそめます。
MarkJ

9
ルセルノは正しいです。このコーディングパターンは、かっこを避けようとすることによって引き起こされます。Lispにさらされた後は不治。
ハンスパッサント2010年

13
最適化されたコード:(f as T).SomeMethod();)
MSalters 2010年

回答:


252

あなたの理解は本当です。それは私にマイクロ最適化を試みているように聞こえます。タイプがわかっている場合は、通常のキャストを使用してください。より実用的な例外を生成することに加えて、それは速く失敗します。型に関する想定が間違っている場合、プログラムはすぐに失敗し、将来のある時点で、NullReferenceExceptionまたはArgumentNullException論理エラーさえ待たなくても、失敗の原因をすぐに確認できます。一般に、どこかでチェックがas続かない式nullはコードのにおいです。

一方、キャストについて確信が持てず、失敗することが予想される場合はastry-catchブロックでラップされた通常のキャストの代わりに使用する必要があります。また、as型チェックの後にキャストするよりも、の使用をお勧めします。の代わりに:

if (x is SomeType)
   ((SomeType)x).SomeMethod();

その生成しisinstた命令のためのisキーワード、およびcastclass命令キャスト(効果的に二倍のキャストを実行する)ために、あなたが使用する必要があります。

var v = x as SomeType;
if (v != null)
    v.SomeMethod();

これはisinst命令を生成するだけです。前の方法は、isチェックが成功し、キャスト行で失敗した後、競合状態により変数の型が変更される可能性があるため、マルチスレッドアプリケーションに潜在的な欠陥があります。後者の方法では、このエラーは発生しません。


次のソリューションは、量産コードでの使用は推奨さません。C#でこのような基本的な構成が本当に嫌いな場合は、VBまたは他の言語への切り替えを検討することをお勧めします。

キャストの構文が大嫌いな場合は、キャストを模倣する拡張メソッドを作成できます。

public static T To<T>(this object o) { // Name it as you like: As, Cast, To, ...
    return (T)o;
}

そして、きちんとした[?]構文を使用します:

obj.To<SomeType>().SomeMethod()

5
競合状態は関係ないと思います。この問題が発生している場合、コードはスレッドセーフではなく、キーワード「as」を使用するよりも信頼性の高い方法で解決できます。残りの答えは+1。
RMorrisey 2010年

10
@RMorrisey:私は少なくとも1つの例を念頭に置いていcacheます。別のスレッドがに設定することによってそれを無効にしようとするオブジェクトがあるとしnullます。ロックフリーのシナリオでは、このようなことが発生する可能性があります。
Mehrdad Afshari、2010年

10
is + castは、FxCopから「不必要にキャストしないでください」という警告をトリガーするのに十分です:msdn.microsoft.com/en-us/library/ms182271.aspxこれは、構造を回避する十分な理由です。
David Schmitt

2
で拡張メソッドを作成することは避けてくださいObject。値タイプでこのメソッドを使用すると、不必要にボックス化されます。
MgSam 2012年

2
@MgSam明らかに、このようなユースケースはTo、継承階層全体で変換するだけなので、ここではメソッドには意味がありません。もちろん、アイデア全体は真面目というより理論的です。
Mehrdad Afshari 2012年


40

'as'を使用してもユーザー定義の変換は適用されませんが、キャストでは適切に変換が使用されます。これは、場合によっては重要な違いになることがあります。


5
これは覚えておくことが重要です。エリックリペットは、ここでその上行く:blogs.msdn.com/ericlippert/archive/2009/10/08/...
Pパパ

5
素敵なコメント、P!ただし、コードがこの区別に依存している場合は、将来的に深夜のデバッグセッションがあると思います。
TrueWill、2010年

36

私はこれについてここに少し書きました:

http://blogs.msdn.com/ericlippert/archive/2009/10/08/what-s-the-difference-between-as-and-cast-operators.aspx

要点を理解しました。そして、私はそれの推力に同意します:キャストオペレーターは「このオブジェクトはその型に変換できると確信しています。私が間違っていれば例外を危険にさらすつもりです。 「このオブジェクトがその型に変換できるかどうかわかりません。間違っている場合はnullを指定してください」。

ただし、微妙な違いがあります。(x as T).Whatever()は、「xをTに変換できることだけでなく、参照変換またはボックス化解除変換のみを含み、さらにxがnullでないことを知っています」と伝えます。これは((T)x).Whatever()とは異なる情報を伝達します。おそらくそれがコードの作成者の意図です。


1
コードの作者をあなたの最後の文で投機的に防御したことに同意しません。 nullでないこと((T)x).Whatever() 伝えxます。作成者がT参照変換またはボックス化変換のみで変換が行われるか、またはユーザー定義変換または表現変更変換が必要かどうか、通常は作成者が気にすることを強く疑います。結局のところ、私がを定義するとpublic static explicit operator Foo(Bar b){}Barと互換性があると見なされるのは明らかに私の意図ですFoo。この変換を避けたいと思うことはまれです。
Pダディ

4
まあ、おそらくコードのほとんどの作者はその微妙な区別をしていません。個人的にはそうかもしれませんが、もしそうなら、その影響についてコメントを追加します。
Eric Lippert、2010年

16

私はこの誤解を招く記事への言及を、キャストよりも「として」の方が速いという証拠としてよく見ました。

この記事のより紛らわしい誤解を招く側面の1つは、測定されているものを示さないグラフィックです。私はそれが失敗したキャストを測定ていると思います(例外はスローされないため、「as」は明らかにはるかに高速です)。

時間をかけて測定を行うと、予想どおり、キャストが成功したときの "as" よりもキャストが高速であることがわかります。

これが、キャストの代わりにasキーワードを「カーゴカルト」として使用する理由の1つかもしれないと思います。


2
リンクをありがとう、それは非常に興味深いです。私が記事を理解した方法から、彼例外でないケースを比較します。それにもかかわらず、記事は.net 1.1向けに書かれており、コメントは、これが.net 2.0で変更されたことを指摘しています。パフォーマンスはほぼ同じになり、接頭辞キャストがわずかに高速化されています。
ハインツィ

1
この記事は、彼が例外ではないケースを比較していることを示唆していますが、私はずっと前にいくつかのテストを行ったため、.NET 1.xを使用しても、主張された結果を再現できませんでした。また、この記事ではベンチマークを実行するために使用されるコードを提供していないため、比較対象を言うことは不可能です。
Joe

「カーゴカルト」-完璧。詳細については、「Cargo Cult Science Richard Feynman」をご覧ください。
ボブ・デニー

11

直接キャストには、asキーワードよりも括弧のペアが必要です。そのため、タイプが100%確実である場合でも、視覚的な混乱を減らします。

しかし、例外的なことについては同意しました。しかし、少なくとも私にとっては、後でas確認するためにボイルダウンのほとんどの使用法はnull、例外をキャッチするよりも良いと思います。


8

「として」を使用する99%の時間は、実際のオブジェクトタイプが何かわからないときです

var x = obj as T;
if(x != null){
 //x was type T!
}

また、明示的なキャスト例外をキャッチしたり、 "is"を使用して2回キャストしたりしたくありません。

//I don't like this
if(obj is T){
  var x = (T)obj; 
}

8
の適切な使用例について説明しましたas。他の1%は?
Pダディ

タイプミスですか?=)私はこの正確なコードスニペットを使用する時間の99%を意味しましたが、メソッドの呼び出しや他の場所で "as"を使用することがあります。
Max Galkin、2010年

ドー、そしてそれは2番目の人気のある回答よりもどのように役に立たないのですか?
Max Galkin、2010年

2
+1私はこれを呼び出すことはルーベンスファリアスの答えと同じくらい価値があることに同意します-人々がうまくいけばここに来て、これは有用な例になるでしょう
ルーベンバルテリンク

8

それは人々が見た目が好きだからという理由だけで、それは非常に読みやすいです。

それに直面しましょう:Cのような言語でのキャスト/変換演算子は、かなりひどく、読みやすくなっています。C#が次のJavascript構文のいずれかを採用した方がいいと思います。

object o = 1;
int i = int(o);

またはto、次のキャストに相当する演算子を定義しますas

object o = 1;
int i = o to int;

ご存知のように、あなたが言及したJavaScript構文はC ++でも使用できます。
Pダディ

@PDaddy:直接的な100%互換性のある代替構文ではありませんが、そのようには意図されていません(演算子Xと変換コンストラクタ)
Ruben Bartelink

dynamic_cast<>()(および類似の)C ++構文を使用することをお勧めします。あなたは醜い何かをしている、それは醜く見えるはずです。
トムホーティン-タックライン

5

as例外から安全に感じるので、人々はとても好きです...ボックスの保証のように。男は箱に豪華な保証をします。なぜなら彼はあなたにすべての暖かくてトーストを感じて欲しいからです。あなたはその小さな箱を夜枕の下に置いたと思います、保証の妖精が降りてきて四分の一を離れるかもしれません、私は正しいテッドですか?

トピックに戻る...直接キャストを使用する場合、無効なキャスト例外が発生する可能性があります。したがって、人々は(それ自体では)例外をスローしないasため、すべてのキャスティングニーズに包括的なソリューションとして適用されasます。しかし、これについておもしろいの(x as T).SomeMethod();は、無効なキャスト例外をnull参照例外と交換している例です。例外を表示すると、実際の問題がわかりにくくなります。

通常はあまり使いasません。私isにとっては、キャストを試してnullをチェックするよりも読みやすく、より意味があるため、テストを好みます。


2
「私はisテストを好む」-「is」の後にキャストが続くのはもちろん「as」の後にnullが続くテストよりも遅い(「IDictionary.ContainsKey」のようにインデクサーを使用して逆参照するのが「IDictionary.TryGetValue」より遅い")。しかし、それがより読みやすくなっている場合、間違いなくその違いはめったにありません。
Joe

真ん中の重要な声明は、人々asが安心感を与えるので、人々が毛布の解決策としてどのように適用するかです。
Bob

5

これは私の一番のおしっこの 1つでなければなりませ

StroustrupのD&Eおよび/または私が今見つけることができないいくつかのブログ投稿でtoは、https: //stackoverflow.com/users/73070/johannes-rossel によって作成されたポイントに対応する演算子の概念について説明しています(つまり、構文は同じですasが、DirectCastセマンティクスを使用しています) )。

これが実装されなかった理由は、キャストが痛みを引き起こし、醜くなければならないため、キャストを使用しないようにするためです。

「賢い」プログラマー(本の著者(Juval Lowy IIRC)であることが多い)asがこの方法で悪用することによってこれを回避するのは残念です(asおそらくこの理由でC ++はを提供していません)。

でもVBは、あなたが選択する力という均一な構文を持つ、より一貫性があるTryCastか、DirectCastあなたの心を作るに


+1。おそらく、構文ではなくDirectCast 動作を意味します
ハインツィ

@Heinzi:Ta + 1。いい視点ね。smartarseで、semantics代わりに使用することを決定しました:P
Ruben Bartelink 2010年

C#にはC、C ++、またはJavaとの互換性がないということを考えると、これらの言語から借りてきたもののいくつかに不満を感じています。「これはXであることを知っています」、「これはXではないことを知っていますが、1つとして表すことができます」、「これはXでないことを知っており、実際には1つとして表現できない場合があります」とにかくXをくれ。」がに収まる正確な値を表しdouble-to-intdoubleいない場合に失敗するキャストの有用性を確認できますInt32が、(int)-1.5利回り-1を指定することは単に醜いことです。
スーパーキャット2014

@supercatはい、しかし、誰もが知っているように、言語設計は簡単ではありません-C#nullableに関連する一連のトレードオフを見てください。知られている唯一の解毒剤はDepthエディションでC#を定期的に読むことです:)ありがたいことに、最近のF#のニュアンスを理解することにもっと関心があり、これらの問題の多くについてもっと正気です。
Ruben Bartelink 2014

@RubenBartelink:私はかなりNULL可能タイプは解決になっていた問題を正確なものをクリアしていないんだけど、私はほとんどの場合、持っているよりよいだったと思うだろうMaybeValid<T>2つのパブリックフィールドを持つをIsValidしてValueいるコードは、それがフィット感を見ているようで行うことができます。それは例えば許可されたでしょうMaybeValid<TValue> TryGetValue(TKey key) { var ret = default(MaybeValid<TValue>); ret.IsValid = dict.TryGetValue(key, out ret.Value); return ret; }。と比較してNullable<T>、少なくとも2つのコピー操作を節約できるだけでなく、クラスだけでなくあらゆるタイプの価値が得られTます。
スーパーキャット2014

2

私はと信じているasキーワードが、よりエレガントな探してバージョンと考えることができ dynamic_castC ++から。


C#でのストレートキャストはdynamic_cast、C ++に似ています。
Pダディ

C#のストレートキャストは、C ++のstatic_castと同等であると思います。
Andrew Garrison

2
@Ruben Bartelink:ポインタのあるnullのみを返します。可能な場合に使用する必要がある参照を使用すると、がスローされstd::bad_castます。
Pダディ

1
@Andrew Garrison:static_cast実行時の型チェックを実行しません。C#にはこれに似たキャストはありません。
Pダディ

悲しいことに、ポインターでキャストを使用したことがあるので、参照でキャストを使用できることさえ知りませんでしたが、Pダディーは完全に正しいです!
Andrew Garrison

1

技術的な理由がないので、おそらくもっと人気がありますが、それは読みやすく、より直感的だからです。(質問に答えようとするだけで良くなるとは言わない)


1

「として」を使用する1つの理由:

T t = obj as T;
 //some other thread changes obj to another type...
if (t != null) action(t); //still works

(悪いコード)の代わりに:

if (obj is T)
{
     //bang, some other thread changes obj to another type...
     action((T)obj); //InvalidCastException
}

2
あなたがこの醜い醜い競争条件を持っているなら、あなたはより大きな問題を抱えています(しかし、他の人たちと一緒に行くのに良いサンプルに同意してください+1
Ruben Bartelink

これは誤りを永続させるため、-1。他のスレッドがobjのタイプを変更している可能性がある場合は、まだ問題があります。tはTへのポインターとして使用されるため、 "// still works"の主張は真実であるとは言えませんが、Tでなくなったメモリを指します。他のスレッドがaction(t)の進行中のobj。
スティーブンC.スチール

5
@Stephen C. Steel:かなり混乱しているようですね。のタイプを変更すると、別のオブジェクトへの参照を保持するように変数自体をobj変更することになりobjます。によって最初に参照されたオブジェクトが常駐するメモリの内容は変更されませんobj。この元のオブジェクトは変更されずに残り、t変数はそのオブジェクトへの参照を保持します。
Pダディ


1
@Pダディ-私はあなたが正しいと思います、そして私は間違っていました:objがTオブジェクトからT2オブジェクトに再バインドされた場合、tはまだ古いTオブジェクトを指しています。tはまだ古いオブジェクトを参照しているため、ガベージコレクションを実行できないため、古いTオブジェクトは有効なままです。私の競合状態検出回路はC ++でトレーニングされており、dynamic_castを使用する同様のコードが潜在的な問題となります。
スティーブンC.スチール
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.