CLRでのキャストと「as」キーワードの使用


387

インターフェイスをプログラミングするとき、キャストやオブジェクトタイプの変換をたくさん行っていることがわかりました。

これらの2つの変換方法に違いはありますか?もしそうなら、費用の違いはありますか、またはこれは私のプログラムにどのように影響しますか?

public interface IMyInterface
{
    void AMethod();
}

public class MyClass : IMyInterface
{
    public void AMethod()
    {
       //Do work
    }

    // Other helper methods....
}

public class Implementation
{
    IMyInterface _MyObj;
    MyClass _myCls1;
    MyClass _myCls2;

    public Implementation()
    {
        _MyObj = new MyClass();

        // What is the difference here:
        _myCls1 = (MyClass)_MyObj;
        _myCls2 = (_MyObj as MyClass);
    }
}

また、「一般的に」推奨される方法は何ですか?


最初にキャストを使用する理由の簡単な例を質問に追加できますか、または新しいキャストを開始できますか?なぜユニットテストのみにキャストが必要になるのか、ちょっと興味があります。それはこの質問の範囲外だと思います。
エリックファンブラケル

2
この必要性を防ぐために、おそらくユニットテストを変更できます。基本的には、インターフェイスにない具体的なオブジェクトのプロパティがあるということです。私はそのプロパティを設定する必要がありますが、そのプロパティは他の方法で設定されていたはずです。それはあなたの質問に答えますか?
フランクV

パトリックHägneが鋭く、以下の指摘するように、そこIS違い。
ニール、

回答:


519

行の下の答えは2008年に書かれました。

C#7では、パターンマッチングが導入されました。これはas、次のように記述できるように、演算子に代わるものです。

if (randomObject is TargetType tt)
{
    // Use tt here
}

ttこの後範囲ではまだですが、確かに割り当てられていません。(これif本体内確実に割り当てられます。)場合によっては少し煩わしいので、すべてのスコープで可能な限り少ない数の変数を導入することに本当に関心がある場合はis、キャストを続けて使用することもできます。


これまでの回答(この回答を開始した時点では!)は、どれを使用する価値があるかを本当に説明しているとは思いません。

  • これを行わないでください:

    // Bad code - checks type twice for no reason
    if (randomObject is TargetType)
    {
        TargetType foo = (TargetType) randomObject;
        // Do something with foo
    }

    このチェックは2回行われるだけでrandomObjectなく、ローカル変数ではなくフィールドの場合、さまざまなチェックを行う場合があります。「if」が通過しても、別のスレッドがrandomObject2つの値を変更すると、キャストが失敗する可能性があります。

  • 場合はrandomObject、本当に必要があるのインスタンスでTargetType、それは、その後、鋳造、手段はバグがあること、ない場合、すなわち適切なソリューションです。これにより、すぐに例外がスローされます。つまり、誤った仮定の下ではこれ以上の作業は行われず、例外はバグのタイプを正しく示します。

    // This will throw an exception if randomObject is non-null and
    // refers to an object of an incompatible type. The cast is
    // the best code if that's the behaviour you want.
    TargetType convertedRandomObject = (TargetType) randomObject;
  • 場合randomObject かもしれないのインスタンスであるTargetTypeTargetType参照型である、このようなコードを使用します。

    TargetType convertedRandomObject = randomObject as TargetType;
    if (convertedRandomObject != null)
    {
        // Do stuff with convertedRandomObject
    }
  • 場合randomObject かもしれないのインスタンスであるTargetTypeTargetType値型である、我々は使用できないasTargetType、それ自体が、我々はNULL可能タイプを使用することができます。

    TargetType? convertedRandomObject = randomObject as TargetType?;
    if (convertedRandomObject != null)
    {
        // Do stuff with convertedRandomObject.Value
    }

    (注:現在、これは+キャストよりも実際には低速です。よりエレガントで一貫性があると思いますが、それで完了です。)

  • 変換された値が本当に必要ないが、それ TargetTypeのインスタンスであるかどうかを知る必要があるだけの場合、isオペレーターはあなたの友達です。この場合、TargetTypeが参照型であるか値型であるかは関係ありません。

  • is(Tが参照型であるかどうかがわからない場合があるため、次のように使用できないため)ジェネリックが関係する他のケースもあるかもしれませんが、比較的あいまいです。

  • 私はこれまでほとんど確実にis値型のケースを使用してきましたが、null許容型とas一緒に使用することは考えていませんでした:)


編集:上記のいずれも、値型の場合を除いて、パフォーマンスについて説明していないことに注意してください。ここで、null許容値型へのボックス化解除は実際には低速ですが、一貫しています。

naaskingの回答によると、is-and-castまたはis-and-asはどちらも、以下のコードに示すように、最新のJITでas-and-null-checkと同じくらい高速です。

using System;
using System.Diagnostics;
using System.Linq;

class Test
{
    const int Size = 30000000;

    static void Main()
    {
        object[] values = new object[Size];
        for (int i = 0; i < Size - 2; i += 3)
        {
            values[i] = null;
            values[i + 1] = "x";
            values[i + 2] = new object();
        }
        FindLengthWithIsAndCast(values);
        FindLengthWithIsAndAs(values);
        FindLengthWithAsAndNullCheck(values);
    }

    static void FindLengthWithIsAndCast(object[] values)        
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            if (o is string)
            {
                string a = (string) o;
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("Is and Cast: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithIsAndAs(object[] values)        
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            if (o is string)
            {
                string a = o as string;
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("Is and As: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithAsAndNullCheck(object[] values)        
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            string a = o as string;
            if (a != null)
            {
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("As and null check: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }
}

私のラップトップでは、これらはすべて約60msで実行されます。注意すべき2つの点:

  • それらの間に大きな違いはありません。(実際には、としてプラスヌルチェックは間違いなくする状況があるでは、。遅くなり、それは密封されたクラスのためだから上記のコードは、実際に型チェック容易になります。もしあなたのインターフェイスのためにしているチェック、バランスのヒントわずかas-plus-null-checkを支持します。)
  • それらはすべてめちゃくちゃ速いです。これは、単純ではないでしょう、あなたが本当にやろうとしていないされていない限り、あなたのコード内のボトルネックになることは何も、その後値を持ちます。

したがって、パフォーマンスについては心配しないでください。正確さと一貫性について心配しましょう。

is-and-cast(またはis-and-as)は、変数を処理するときにどちらも安全ではないことを維持します。テストとキャストの間の別のスレッドにより、参照する値の型が変わる可能性があるためです。それはかなりまれな状況ですが、私は一貫して使用できる規則を持っていると思います。

また、null-as-then-null-checkにより、懸念をより適切に分離できると私は主張します。変換を試みる1つのステートメントと、その結果を使用する1つのステートメントがあります。IS-とキャスト又はあるアンドとして機能試験と、その後値を変換するための別の試み。

別の言い方をすれば、誰かがこれまでに書いたでしょう:

int value;
if (int.TryParse(text, out value))
{
    value = int.Parse(text);
    // Use value
}

is-and-castがやっていることのようなものですが、明らかにかなり安価です。


7
ここではILの面である/など/鋳物のコストは、次のとおりです。atalasoft.com/cs/blogs/stevehawley/archive/2009/01/30/...
台座

3
場合で、targetObject ターゲットタイプである可能性がある場合、「is」とキャストの組み合わせの使用が悪い習慣と見なされるのはなぜですか?つまり、遅いコードを生成しますが、この場合、「targetObjectがnullの場合は何かを実行する」ではなく、「targetObjectがtargetTypeの場合は何かを実行する」のように、ASキャストよりも意図が明確です。さらに、AS句は不要な変数を作成しますIFスコープ外。
Valera Kolupaev、

2
@Valera:良い点ですが、as / nullテストは十分に慣用的であるため、ほとんどすべてのC#開発者に意図が明確であるべきです。+キャストに含まれる重複が個人的に好きではありません。私は実際には、両方のアクションを1つで実行する一種の「as-if」構成が必要です。彼らは頻繁に一緒に行きます...
Jon Skeet

2
@ジョンスキート:ごめんなさい。IsAnd Cast:2135、Is And As:2145、As And nullチェック:1961、仕様:OS:Windows Seven、CPU:i5-520M、4GBのDDR3 1033 ram、アレイのベンチマーク128,000,000アイテム。
Behrooz、

2
C#7をif (randomObject is TargetType convertedRandomObject){ // Do stuff with convertedRandomObject.Value}使用すると、次のことができます。または、switch/ case docsを参照
WerWet

72

"as"は、キャストできない場合はNULLを返します。

beforeキャ​​ストは例外を発生させます。

パフォーマンスを向上させるために、例外の発生は通常、時間のかかるコストが高くなります。


3
例外の調達は、より高価ですが、あなたは、オブジェクトが正しくキャストできることがわかっている場合は、として(アントンの応答を参照)ので、安全点検のより多くの時間を要します。しかし、安全チェックのコストはかなり小さいと思います。

17
例外を発生させる可能性のあるコストは考慮すべき要素ですが、多くの場合それは正しい設計です。
Jeffrey L Whitledge、2009年

@panesofglass-参照型の場合、変換の互換性は実行時にasとcastの両方で常にチェックされるため、係数は2つのオプションを区別しません。(そうでない場合、キャストは例外を発生させることができませんでした。)
ジェフリーLホイットリッジ、2009年

4
@Frank-たとえば、ジェネリック前のコレクションを使用する必要があり、APIのメソッドが従業員のリストを必要とし、一部のジョーカーが製品のリストを渡す場合、無効なキャスト例外がシグナルに適切な場合があります。インターフェース要件の違反。
Jeffrey L Whitledge、2009年

27

ILを比較した別の回答を次に示します。クラスを考えてみましょう:

public class MyClass
{
    public static void Main()
    {
        // Call the 2 methods
    }

    public void DirectCast(Object obj)
    {
        if ( obj is MyClass)
        { 
            MyClass myclass = (MyClass) obj; 
            Console.WriteLine(obj);
        } 
    } 


    public void UsesAs(object obj) 
    { 
        MyClass myclass = obj as MyClass; 
        if (myclass != null) 
        { 
            Console.WriteLine(obj);
        } 
    }
}

次に、各メソッドが生成するILを確認します。opコードが意味をなさない場合でも、大きな違いが1つあります。isinstが呼び出され、その後にDirectCastメソッドのcastclassが呼び出されます。つまり、基本的に1つではなく2つの呼び出しです。

.method public hidebysig instance void  DirectCast(object obj) cil managed
{
  // Code size       22 (0x16)
  .maxstack  8
  IL_0000:  ldarg.1
  IL_0001:  isinst     MyClass
  IL_0006:  brfalse.s  IL_0015
  IL_0008:  ldarg.1
  IL_0009:  castclass  MyClass
  IL_000e:  pop
  IL_000f:  ldarg.1
  IL_0010:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_0015:  ret
} // end of method MyClass::DirectCast

.method public hidebysig instance void  UsesAs(object obj) cil managed
{
  // Code size       17 (0x11)
  .maxstack  1
  .locals init (class MyClass V_0)
  IL_0000:  ldarg.1
  IL_0001:  isinst     MyClass
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  brfalse.s  IL_0010
  IL_000a:  ldarg.1
  IL_000b:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_0010:  ret
} // end of method MyClass::UsesAs

isinstキーワードとcastclass

このブログ投稿では、2つの方法を適切に比較しています。彼の要約は:

  • 直接比較すると、isinstはcastclassよりも高速です(わずかですが)
  • 変換が成功したことを確認するためにチェックを実行する必要がある場合、isinstはcastclassよりも大幅に高速でした
  • isinstとcastclassの組み合わせは使用しないでください。これは最も速い「安全な」変換よりもはるかに遅いため(12%以上遅い)

私は個人的に常にAsを使用しています。これは読みやすく、.NET開発チーム(またはJeffrey Richter)から推奨されているためです。


キャスティングvs asの明確な説明を探していましたが、この答えは、一般的な中間言語の段階的な説明に関係しているため、より明確になります。ありがとう!
モース

18

2つの間の微妙な違いの1つは、キャスト演算子が含まれている場合、「as」キーワードをキャストに使用できないことです。

public class Foo
{
    public string Value;

    public static explicit operator string(Foo f)
    {
        return f.Value;
    }

}

public class Example
{
    public void Convert()
    {
        var f = new Foo();
        f.Value = "abc";

        string cast = (string)f;
        string tryCast = f as string;
    }
}

「as」キーワードはキャスト演算子を考慮しないため、これは最終行でコンパイルされません(以前のバージョンではコンパイルされたと思います)。ラインstring cast = (string)f;はうまくいきます。


12

それが戻って変換を実行することができない場合に例外をスローすることはありませんヌルを(代わりとしてのみ参照型で動作します)。したがって、asを使用すると、基本的に

_myCls2 = _myObj is MyClass ? (MyClass)_myObj : null;

一方、Cスタイルのキャストは、変換が不可能な場合に例外をスローします。


4
同等です。ただし、同じではありません。これにより、より多くのコードが生成されます。
台座、

10

あなたの質問に対する答えではありませんが、私が重要だと思うことは関連するポイントです。

インターフェイスにプログラミングしている場合は、キャストする必要はありません。うまくいけば、これらのキャストは非常にまれです。そうでない場合は、インターフェイスの一部を再考する必要がある可能性があります。


これまでのところ、キャスティングは私のユニットテストでほとんど必要でしたが、それを取り上げてくれてありがとう。これに取り組む間、私はそれを心に留めておきます。
フランクV

ヒキガエルに同意します。ユニットテストの側面が@Frank Vのキャスティングに関連している理由にも興味があります。別の方法で管理する必要があるさまざまな問題を解決するため。
上院議員

@TheSenatorこの質問は3年以上前なので、覚えていません。しかし、私はおそらくユニットテストの時でさえも積極的にインターフェースを使用していました。おそらく、ファクトリパターンを使用していて、テストするターゲットオブジェクトのパブリックコンストラクタにアクセスできなかったためです。
フランクV

9

Jon Skeetのアドバイスを無視してください、つまり、テストアンドキャストパターンを避けてください。

if (randomObject is TargetType)
{
    TargetType foo = randomObject as TargetType;
    // Do something with foo
}

これはキャストとnullテストよりもコストがかかるという考えはMYTHです。

TargetType convertedRandomObject = randomObject as TargetType;
if (convertedRandomObject != null)
{
    // Do stuff with convertedRandomObject
}

これは機能しないマイクロ最適化です。私は実際のテストをいくつか実行しましたが、テストアンドキャストは実際にはキャストアンドヌル比較よりも高速です。不合格。

テストアンドキャストの方が速い、または少なくとも遅くない理由が必要な場合は、単純で複雑な理由があります。

単純:単純なコンパイラーでも、テストとキャストなどの2つの類似した操作を1つのテストとブランチに統合します。cast-and-null-testは、2つのテストと1つの分岐を強制する場合があります。1つはタイプテストと、失敗時のnullへの変換、1つはnullチェック自体です。少なくとも、どちらも単一のテストと分岐に最適化されるため、テストとキャストはキャストとnullテストよりも遅くも速くもありません。

複雑:テストとキャストの方が速い理由:キャストとnullのテストでは、コンパイラーが活性を追跡する必要がある別の変数が外部スコープに導入され、コントロールの複雑さによってはその変数を最適化できない場合があります。流れです。逆に、テストアンドキャストでは、区切られたスコープ内にのみ新しい変数が導入されるため、スコープの終了後に変数が無効であることをコンパイラが認識し、レジスタ割り当てを最適化できます。

したがって、どうか、この「キャストアンドヌルテストはテストアンドキャストよりも優れている」というアドバイスをDIEにお任せください。お願いします。テストアンドキャストは、安全かつ高速です。


7
@naasking:(最初のスニペットのように)2回テストすると、フィールドまたはrefパラメーターの場合、2つのテストの間で型が変わる可能性があります。ローカル変数に対しては安全ですが、フィールドに対しては安全ではありません。ベンチマークを実行したいのですが、ブログ投稿で提供したコードは完全ではありません。マイクロ最適化しないことに同意しますが、値を2回使用することは、 "as"とnullityテストを使用するよりも読みやすくエレガントではないと思います。(私は間違いなく、「as」の後ではなく、ストレートキャストを使用します。)
Jon Skeet

5
なぜそれがより安全であるのかもわかりません。実際、安全性が低い理由を示しました。確かに、スコープ内の変数はnullになる可能性がありますが、後続の "if"ブロックのスコープ外で変数を使用しない限り、問題はありません。私が提起した安全性の懸念(フィールドの値を変更することについて)は、表示れたコードの真の懸念です -あなたの安全性の懸念は、開発者が他のコードに甘んじていることを要求します。
Jon Skeet

1
is / castまたはas / castを指摘するための+1は、実際には遅くありません。自分で完全なテストを実行したので、私が見る限り、問題がないことを確認できます。率直に言って、非常に短い時間で膨大な数のキャストを実行できます。完全なコードで私の答えを更新します。
Jon Skeet

1
実際、バインディングがローカルでない場合、TOCTTOUバグ(チェックの時間から使用の時間)のバグが発生する可能性があります。なぜそれがより安全なのかについては、何らかの理由でローカルを再利用したい多くのジュニア開発者と協力しています。したがって、キャストアンドヌルは私の経験では非常に現実的な危険であり、コードをそのように設計していないので、TOCTTOUの状況に遭遇したことはありません。ランタイムテストの速度については、仮想ディスパッチよりも高速です[1]。Re:コード、キャストテストのソースが見つかるかどうかを確認します。[1] higherlogics.blogspot.com/2008/10/...
naasking

1
@naasking:私はローカルの再利用の問題に遭遇したことはありません-しかし、コードレビューでは、より微妙なTOCTTOUのバグよりも簡単に特定できると思います。封印されたクラスではなく、インターフェイスの独自のベンチマークチェックを再実行しただけであり、as-then-null-checkを優先してパフォーマンスをヒントしていることも指摘しておく必要があります。ここで特定のアプローチを選択するのはそのためです。
Jon Skeet、2010年

4

キャストが失敗した場合、「as」キーワードは例外をスローしません。代わりに、変数をnull(または値タイプのデフォルト値)に設定します。


3
値タイプのデフォルト値はありません。値型のキャストには使用できません。
PatrikHägne、2009年

2
「as」キーワードは実際には値タイプでは機能しないため、常にnullに設定されます。
エリックファンブラケル

4

これは質問への回答ではなく、質問のコード例へのコメントです。

通常、IMyInterfaceなどのオブジェクトをMyClassにキャストする必要はありません。インターフェイスの優れた点は、インターフェイスを実装する入力としてオブジェクトを取得する場合、取得するオブジェクトの種類を気にする必要がないことです。

IMyInterfaceをMyClassにキャストする場合、タイプMyClassのオブジェクトを取得し、IMyInterfaceを使用しても意味がありません。IMyInterfaceを実装する他のクラスでコードをフィードすると、コードが破損するためです...

さて、私のアドバイス:インターフェイスが適切に設計されていれば、多くの型キャストを回避できます。


3

asオペレータは、それが過負荷状態にすることができない、参照型で使用することができ、それが返されnull、操作が失敗した場合。例外はスローされません。

キャストは互換性のあるすべての型で使用でき、オーバーロードされる可能性があり、操作が失敗すると例外がスローされます。

どちらを使用するかは、状況によって異なります。主に、失敗した変換で例外をスローするかどうかの問題です。


1
'as'は、興味深いパターンを提供するnull許容値型にも使用できます。コードについては私の答えを参照してください。
Jon Skeet、

1

私の答えは、型をチェックせず、キャスト後にnullをチェックしない場合の速度のみです。Jon Skeetのコードに2つのテストを追加しました。

using System;
using System.Diagnostics;

class Test
{
    const int Size = 30000000;

    static void Main()
    {
        object[] values = new object[Size];

        for (int i = 0; i < Size; i++)
        {
            values[i] = "x";
        }
        FindLengthWithIsAndCast(values);
        FindLengthWithIsAndAs(values);
        FindLengthWithAsAndNullCheck(values);

        FindLengthWithCast(values);
        FindLengthWithAs(values);

        Console.ReadLine();
    }

    static void FindLengthWithIsAndCast(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            if (o is string)
            {
                string a = (string)o;
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("Is and Cast: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithIsAndAs(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            if (o is string)
            {
                string a = o as string;
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("Is and As: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithAsAndNullCheck(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            string a = o as string;
            if (a != null)
            {
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("As and null check: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }
    static void FindLengthWithCast(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            string a = (string)o;
            len += a.Length;
        }
        sw.Stop();
        Console.WriteLine("Cast: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithAs(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            string a = o as string;
            len += a.Length;
        }
        sw.Stop();
        Console.WriteLine("As: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }
}

結果:

Is and Cast: 30000000 : 88
Is and As: 30000000 : 93
As and null check: 30000000 : 56
Cast: 30000000 : 66
As: 30000000 : 46

これは非常に高速であるため、(私がしたように)速度に集中しようとしないでください。


同様に、私のテストでは、as変換(エラーチェックなし)がキャストよりも約1〜3%高速であることがわかりました(約540ミリ秒に対して1億回の反復で550ミリ秒)。どちらもあなたのアプリケーションを作成または破壊しません。
palswim 2016

1

ここですでに公開されているすべてのものに加えて、明示的なキャスト間で、注目に値する実用的な違いがあることに気づきました

var x = (T) ...

as演算子を使用する場合と比較して。

次に例を示します。

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(GenericCaster<string>(12345));
        Console.WriteLine(GenericCaster<object>(new { a = 100, b = "string" }) ?? "null");
        Console.WriteLine(GenericCaster<double>(20.4));

        //prints:
        //12345
        //null
        //20.4

        Console.WriteLine(GenericCaster2<string>(12345));
        Console.WriteLine(GenericCaster2<object>(new { a = 100, b = "string" }) ?? "null");

        //will not compile -> 20.4 does not comply due to the type constraint "T : class"
        //Console.WriteLine(GenericCaster2<double>(20.4));
    }

    static T GenericCaster<T>(object value, T defaultValue = default(T))
    {
        T castedValue;
        try
        {
            castedValue = (T) Convert.ChangeType(value, typeof(T));
        }
        catch (Exception)
        {
            castedValue = defaultValue;
        }

        return castedValue;
    }

    static T GenericCaster2<T>(object value, T defaultValue = default(T)) where T : class
    {
        T castedValue;
        try
        {
            castedValue = Convert.ChangeType(value, typeof(T)) as T;
        }
        catch (Exception)
        {
            castedValue = defaultValue;
        }

        return castedValue;
    }
}

結論 GenericCaster2は構造体タイプでは機能しません。GenericCasterはします。


1

.NET Framework 4.Xを対象とするOffice PIAを使用する場合は、asキーワードを使用する必要があります。そうしないと、コンパイルされません。

Microsoft.Office.Interop.Outlook.Application o = new Microsoft.Office.Interop.Outlook.Application();
Microsoft.Office.Interop.Outlook.MailItem m = o.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem) as Microsoft.Office.Interop.Outlook.MailItem;

ただし、.NET 2.0を対象とする場合、キャストは問題ありません。

Microsoft.Office.Interop.Outlook.MailItem m = (Microsoft.Office.Interop.Outlook.MailItem)o.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem);

.NET 4.Xを対象とする場合、エラーは次のとおりです。

エラーCS0656:コンパイラーが必要なメンバー 'Microsoft.CSharp.RuntimeBinder.Binder.Convert'がありません

エラーCS0656:コンパイラーが必要なメンバー 'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create'がありません


0

asキーワードは、変換が失敗した場合、それは例外を発生させていない主な違いと互換性の参照型間の明示的なキャストと同じように動作します。むしろ、ターゲット変数にnull値を生成します。例外はパフォーマンスの点で非常に高価であるため、キャストのはるかに優れた方法と見なされます。


同じではありません。1つはCastClassを呼び出し、もう1つはILコードでIsInstを呼び出します。
Jenix 2016年

0

何を選択するかは、必要なものによって大きく異なります。明示的なキャストを好む

IMyInterface = (IMyInterface)someobj;

オブジェクトがIMyInterfaceタイプである必要があり、そうでない場合-それは間違いなく問題です。副作用を修正する代わりに正確なエラーを修正するため、できるだけ早くエラーを取得することをお勧めします。

ただし、objectパラメーターとして受け入れるメソッドを扱う場合は、コードを実行する前に、その正確な型を確認する必要があります。そのような場合asは、回避できるので便利ですInvalidCastException


0

それは、 "as"を使用した後にnullをチェックするか、それともアプリが例外をスローすることを望みますか?

私の経験則では、キャストを使用するときに、変数が期待する型であると常に期待しています。変数が希望どおりにキャストされない可能性があり、asを使用してnullを処理する準備ができている場合は、asを使用します。



0

OPの問題は、特定のキャスト状況に限定されます。タイトルはより多くの状況をカバーしています。
以下は、私が現在考えることができるすべての関連するキャスト状況の概要です。

private class CBase
{
}

private class CInherited : CBase
{
}

private enum EnumTest
{
  zero,
  one,
  two
}

private static void Main (string[] args)
{
  //########## classes ##########
  // object creation, implicit cast to object
  object oBase = new CBase ();
  object oInherited = new CInherited ();

  CBase oBase2 = null;
  CInherited oInherited2 = null;
  bool bCanCast = false;

  // explicit cast using "()"
  oBase2 = (CBase)oBase;    // works
  oBase2 = (CBase)oInherited;    // works
  //oInherited2 = (CInherited)oBase;   System.InvalidCastException
  oInherited2 = (CInherited)oInherited;    // works

  // explicit cast using "as"
  oBase2 = oBase as CBase;
  oBase2 = oInherited as CBase;
  oInherited2 = oBase as CInherited;  // returns null, equals C++/CLI "dynamic_cast"
  oInherited2 = oInherited as CInherited;

  // testing with Type.IsAssignableFrom(), results (of course) equal the results of the cast operations
  bCanCast = typeof (CBase).IsAssignableFrom (oBase.GetType ());    // true
  bCanCast = typeof (CBase).IsAssignableFrom (oInherited.GetType ());    // true
  bCanCast = typeof (CInherited).IsAssignableFrom (oBase.GetType ());    // false
  bCanCast = typeof (CInherited).IsAssignableFrom (oInherited.GetType ());    // true

  //########## value types ##########
  int iValue = 2;
  double dValue = 1.1;
  EnumTest enValue = EnumTest.two;

  // implicit cast, explicit cast using "()"
  int iValue2 = iValue;   // no cast
  double dValue2 = iValue;  // implicit conversion
  EnumTest enValue2 = (EnumTest)iValue;  // conversion by explicit cast. underlying type of EnumTest is int, but explicit cast needed (error CS0266: Cannot implicitly convert type 'int' to 'test01.Program.EnumTest')

  iValue2 = (int)dValue;   // conversion by explicit cast. implicit cast not possible (error CS0266: Cannot implicitly convert type 'double' to 'int')
  dValue2 = dValue;
  enValue2 = (EnumTest)dValue;  // underlying type is int, so "1.1" beomces "1" and then "one"

  iValue2 = (int)enValue;
  dValue2 = (double)enValue;
  enValue2 = enValue;   // no cast

  // explicit cast using "as"
  // iValue2 = iValue as int;   error CS0077: The as operator must be used with a reference type or nullable type
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.