直接キャストvs 'as'演算子?


709

次のコードを検討してください。

void Handler(object o, EventArgs e)
{
   // I swear o is a string
   string s = (string)o; // 1
   //-OR-
   string s = o as string; // 2
   // -OR-
   string s = o.ToString(); // 3
}

3つのタイプのキャスティングの違いは何ですか(そう、3番目はキャスティングではありませんが、意図がわかります)。どちらを優先すべきですか?


1
完全に重複しているわけではありませんが、前の質問でパフォーマンスに関する議論もいくつかあります。
スライスされていない2008

8
4日:string s = Convert.ToString(o); 5日:(string s = $"{o}"または同等string.Formatに以前のC#のフォーム)
Earth Engine

回答:


834
string s = (string)o; // 1

例外InvalidCastExceptionがいる場合oではありませんstring。それ以外の場合は、割り当てosしても、oですnull

string s = o as string; // 2

割り当てnulls場合oではないstringか、場合oですnull。このため、値タイプでは使用できません(nullその場合、演算子は決して戻ることができません)。それ以外の場合は、割り当てos

string s = o.ToString(); // 3

の場合、NullReferenceExceptionoが発生しnullます。どんな型であっても、にo.ToString()返すものは何でも割り当てます。so


ほとんどの変換には1を使用します-シンプルで簡単です。私は2をほとんど使用しない傾向があります。何かが適切なタイプでない場合、通常は例外が発生することを期待するからです。私は、エラーコードを使用する不適切に設計されたライブラリ(たとえば、例外を使用する代わりにnullを返す=エラー)を備えた、このreturn-null型の機能の必要性を見てきました。

3はキャストではなく、単なるメソッド呼び出しです。非文字列オブジェクトの文字列表現が必要な場合に使用します。


2
明示的に定義されている場合、値タイプに「null」を割り当てることができます。例:int?私; 文字列s = "5"; i = s int; // iは5 s = null; i = s int; //私はnullになりました
Anheledir '09 / 09/25

3
RE:Anheledir実際には、最初の呼び出しの後、私はnullになります。文字列の値を取得するには、明示的な変換関数を使用する必要があります。
Guvante 2008

45
RE:Sander実際には、使用する別の非常に良い理由があります。これは、チェックコードを簡略化します(nullをチェックしてからnullと正しい型をチェックするのではなくnullをチェックします)多くの場合、カスタムの1つの例外をスローするので、これは役立ちます。しかし、通話が悪いのでブラインドがかかるのは事実です。
Guvante 2008

5
#2は、入力タイプがわからないEqualsメソッドなどに便利です。ただし、一般的には、はい、1が推奨されます。その上で好ましいが、明らかに:)あなただけのものを期待していたときに1種類に制限するタイプのシステムを使用しているだろう
カルム

6
#2は、特殊な型に対して特定のことを行う可能性があるが、それ以外では何もしないコードがある場合にも役立ちます。
AnthonyWJones 2008

349
  1. string s = (string)o;何かが間違いなく他のものになるはずのときに使用します 。
  2. string s = o as string;何か他のものになる可能性がある場合に使用します。
  3. string s = o.ToString(); それが何であるかは気にしないが、利用可能な文字列表現を使用したいだけの場合に使用します。

1
この答えは良さそうですが、正確ではないかもしれません。
j riv 2017

1
最初の2つは好きですが、3番目のオプションに「それがnullではないことは確かです」を追加します。
Uxonith 2017

2
エルビス(?。)を使用すれば、それを気にする必要がなくなります:obj?.ToString()
Quibblesome

@Quibblesome-素晴らしい答えですが、私はあなたの反論について考えるために立ち止まる必要がありました!その言語が15年以上も前から存在していることは文字通り私の心を吹き飛ばします。私たち全員が上級開発者にC#への切り替えを説得しようとする「エッジの効いた」昨日だったような気がします。
Griswald_911 2018

1
@Quibblesomeいい答え:OPまでスクロールする必要がないように、1/2/3を追加すると、迷惑になりますか?私はSOを使用して、古い回答を投票に従ってランク付けします!
whytheq

29

それが本当にo文字列であるかどうか、そして文字列で何をしたいかを知っているかどうかによります。あなたのコメントがo本当に本当に文字列であることを意味するなら、私はストレート(string)oキャストを好むでしょう-それは失敗する可能性はほとんどありません。

ストレートキャストを使用する最大の利点は、失敗したときにInvalidCastExceptionが発生することです。これにより、問題のほとんどがわかります。

asあれば、オペレータ、o文字列ではない、sに設定されているnullあなたはわからないし、テストしたい場合に便利です、s

string s = o as string;
if ( s == null )
{
    // well that's not good!
    gotoPlanB();
}

ただし、そのテストを実行しない場合は、後で使用sしてNullReferenceExceptionがスローされます。ほとんどすべての行が変数を逆参照し、変数をスローする可能性があるため、これらは一般的であり、実際に発生すると、追跡するのがはるかに困難になる傾向があります。一方、値型(プリミティブ、またはDateTimeなどの構造体)にキャストしようとしている場合は、ストレートキャストを使用する必要があります-機能しasません。

文字列に変換する特別なケースでは、すべてのオブジェクトにがあるToStringので、3番目のメソッドは、oがnullでなく、ToStringメソッドが望みどおりの動作をする場合は問題ないかもしれません。


2
一つのノート-あなたが使用できるasNULL可能値の型。IEはo as DateTime動作しません、しかしo as DateTime?ます...
ジョン・ギブ

if (s is string)代わりに使用しないのはなぜですか?
BornToCode

1
@BornToCode、私にとって、主に個人的な好み。何をしているのかに応じて、多くの場合、isINGの後、とにかくもう一度キャストする必要があります。そのため、isとハードキャストがあります。なんらかの理由で、asnullチェックは私にとって気分が良くなりました。
ブレアコンラッド

9

キャストできる型がわかっている場合は、Cスタイルのキャストを使用します。

var o = (string) iKnowThisIsAString; 

Cスタイルのキャストでのみ、明示的な型強制を実行できることに注意してください。

それが目的のタイプであるかどうかわからない場合、それを使用する場合は、キーワードとして使用します。

var s = o as string;
if (s != null) return s.Replace("_","-");

//or for early return:
if (s==null) return;

は型変換演算子を呼び出さないことに注意しください。オブジェクトがnullでなく、指定されたタイプのネイティブである場合のみ、null以外になります。

ToString()を使用すると、文字列にキャストできない場合でも、人間が読める任意のオブジェクトの文字列表現を取得できます。


3
これは、型変換演算子に関する興味深い小さな落とし穴です。コンバージョンを作成したタイプがいくつかありますが、その場合は注意が必要です。
AnthonyWJones 2008

7

asキーワードは、ASP.NETでFindControlメソッドを使用する場合に適しています。

Hyperlink link = this.FindControl("linkid") as Hyperlink;
if (link != null)
{
     ...
}

これはobject、直接キャストの場合と同じように、型付き変数を操作する必要がなく、キャストする必要がないことを意味します。

object linkObj = this.FindControl("linkid");
if (link != null)
{
     Hyperlink link = (Hyperlink)linkObj;
}

大したことではありませんが、コードの行と変数の割り当てを節約し、さらに読みやすくなっています


6

'as'は 'is'に基づいています。これは、オブジェクトがポリモーフィカルに互換性があるかどうかを実行時にチェックし(基本的にキャストが可能かどうか)、チェックが失敗した場合はnullを返すキーワードです。

これら2つは同等です。

「として」を使用:

string s = o as string;

「is」を使用:

if(o is string) 
    s = o;
else
    s = null;

反対に、cスタイルのキャストは実行時にも行われますが、キャストを行うことができない場合は例外がスローされます。

重要な事実を追加するだけです:

「as」キーワードは参照タイプでのみ機能します。あなたはできません:

// I swear i is an int
int number = i as int;

そのような場合は、キャストを使用する必要があります。


私の間違いを指摘してくれてありがとう、あなたは正しい。回答を編集しました。おっと、ごめんなさい。
セルジオアコスタ

5

2は、派生型へのキャストに役立ちます。

aが動物であると仮定ます。

b = a as Badger;
c = a as Cow;

if (b != null)
   b.EatSnails();
else if (c != null)
   c.EatGrass();

最小限のキャストでフィードを取得ます。


2
@Chirs Moutray、それが常に可能であるとは限りません。特にライブラリの場合はそうです。
減速

5

このページで実行された実験によると:http : //www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as

(このページには「違法な参照元」エラーが表示されることがあるので、表示された場合は更新してください)

結論として、 "as"演算子は通常キャストよりも高速です。時には何倍も速く、時にはやっと速くなります。

私が個人的に「として」ものもより読みやすいです。

したがって、高速で「安全」(例外をスローしない)であり、読みやすくなる可能性があるため、常に「as」を使用することをお勧めします。


4

"(string)o"は、直接キャストがないため、InvalidCastExceptionになります。

"o as string"を指定すると、例外がスローされるのではなく、sがnull参照になります。

"o.ToString()"は、それ自体のキャストではありません。オブジェクトによって、つまり何らかの方法で.netのすべてのクラスによって実装されるメソッドであり、呼び出されたクラスと文字列を返します。

文字列に変換するためのConvert.ToString(someType instanceOfThatType)もあります。ここで、someTypeはタイプのセットの1つであり、基本的にはフレームワークの基本タイプです。


3

私が何かを追加する場合は、与えられたすべての回答が適切です。文字列のメソッドとプロパティ(ToLowerなど)を直接使用するには、次のように書くことはできません。

(string)o.ToLower(); // won't compile

あなただけ書くことができます:

((string)o).ToLower();

しかし、代わりに次のように書くことができます:

(o as string).ToLower();

asオプションは(少なくとも私の意見に)より読みやすいです。


(o as string).ToLower()構文は、as演算子の目的を無効にします。oを文字列にキャストできない場合、これはnull参照例外をスローします。
ジェームズ

@james-しかし、キャストが失敗した場合、as演算子の唯一の目的は例外をスローすることだと誰が言ったのですか?oが文字列であることを知っていて、より簡潔なコードを書きたい場合(o as string).ToLower()は、複数の混乱する大括弧の代わりに使用できます。
BornToCode 2016

asの目的はまったく逆です。キャストが失敗したときに例外をスローしてはならず、nullを返します。あなたのoがnullの値を持つ文字列であるとしましょう。その後何が起こりますか?ヒント-ToLowerの呼び出しは失敗します。
ジェームズ2016

@james-その通りですが、nullにならないことが確実にわかっていて、そのオブジェクトのメソッドにアクセスできるようにコンパイラーにキャストする必要がある場合はどうでしょうか。
BornToCode

1
あなたは間違いなくそれを行うことができますが、あなたの値がnullにならないことを保証するために呼び出し元または外部システムに依存したくないので、それは正確にベストプラクティスではありません。C#6を使用している場合は、(文字列として)できますか?。ToLower()。
ジェームズ2016

3
string s = o as string; // 2

ダブルキャストのパフォーマンスペナルティを回避できるため、推奨されます。


こんにちはクリス、この回答に含まれていたリンクは404になりました...その場所に置きたい交換品があるかどうかわかりませんか?
Matt

3

両者は概念的に異なるようです。

直接鋳造

タイプは厳密に関連している必要はありません。それはすべての種類の味があります。

  • カスタムの暗黙的/明示的キャスト:通常、新しいオブジェクトが作成されます。
  • 暗黙的な値タイプ:情報を失うことなくコピーします。
  • 明示的な値タイプ:コピーと情報が失われる可能性があります。
  • IS-A関係:参照タイプを変更しないと、例外がスローされます。
  • 同じタイプ:「キャストは冗長です」。

オブジェクトが何か他のものに変換されるように感じます。

ASオペレーター

タイプには直接的な関係があります。のように:

  • 参照タイプ:IS-Aリレーションシップオブジェクトは常に同じですが、参照が変更されるだけです。
  • 値の型:ボクシングとnull許容型をコピーします。

オブジェクトを別の方法で処理しようとしているように感じます。

サンプルとIL

    class TypeA
    {
        public int value;
    }

    class TypeB
    {
        public int number;

        public static explicit operator TypeB(TypeA v)
        {
            return new TypeB() { number = v.value };
        }
    }

    class TypeC : TypeB { }
    interface IFoo { }
    class TypeD : TypeA, IFoo { }

    void Run()
    {
        TypeA customTypeA = new TypeD() { value = 10 };
        long longValue = long.MaxValue;
        int intValue = int.MaxValue;

        // Casting 
        TypeB typeB = (TypeB)customTypeA; // custom explicit casting -- IL:  call class ConsoleApp1.Program/TypeB ConsoleApp1.Program/TypeB::op_Explicit(class ConsoleApp1.Program/TypeA)
        IFoo foo = (IFoo)customTypeA; // is-a reference -- IL: castclass  ConsoleApp1.Program/IFoo

        int loseValue = (int)longValue; // explicit -- IL: conv.i4
        long dontLose = intValue; // implict -- IL: conv.i8

        // AS 
        int? wraps = intValue as int?; // nullable wrapper -- IL:  call instance void valuetype [System.Runtime]System.Nullable`1<int32>::.ctor(!0)
        object o1 = intValue as object; // box -- IL: box [System.Runtime]System.Int32
        TypeD d1 = customTypeA as TypeD; // reference conversion -- IL: isinst ConsoleApp1.Program/TypeD
        IFoo f1 = customTypeA as IFoo; // reference conversion -- IL: isinst ConsoleApp1.Program/IFoo

        //TypeC d = customTypeA as TypeC; // wouldn't compile
    }


0

nullになる可能性のある(任意のタイプの)何かの文​​字列表現を取得する場合は、以下のコード行を使用します。コンパクトで、ToString()を呼び出し、nullを正しく処理します。oがnullの場合、sにはString.Emptyが含まれます。

String s = String.Concat(o);

0

誰も言及していないので、キーワードによってJavaに最も近いinstanceOfは次のとおりです。

obj.GetType().IsInstanceOfType(otherObj)

0

string s = (string) o;アプリの論理的なコンテキストがstring唯一の有効なタイプである場合は、ダイレクトキャストを使用します。このアプローチでInvalidCastExceptionは、Fail-fastの原則を取得して実装します。ロジックは、無効なタイプをさらに渡さないように保護されasます。演算子を使用すると、NullReferenceExceptionが発生します。

ロジックがいくつかの異なるタイプのキャストstring s = o as string;を予期している場合は、それをオンにするnullか、is演算子を使用します。

キャストを簡略化するためのC#7.0の新しいクールな機能が登場し、パターンマッチングがチェックされます。

if(o is string s)
{
  // Use string variable s
}

or

switch (o)
{
  case int i:
     // Use int variable i
     break;
  case string s:
     // Use string variable s
     break;
 }

0

C#では、次の2つの形式変換(キャスト)がサポートされています。

|

(履歴書

•与えられた式でvの静的型をcに変換する

•vの動的タイプがcまたはcのサブタイプである場合にのみ可能

•そうでない場合は、InvalidCastExceptionがスローされます。

|

v Cとして

•(c)vの非致命的なバリアント

•したがって、指定された式でvの静的型をcに変換します

•vの動的タイプがcでない場合、またはcのサブタイプである場合はnullを返します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.