[解決済み] CLRのキャストと'as'キーワードの使用について
質問
インターフェースをプログラミングする際、キャストやオブジェクトの型変換をすることが多いのですが、どのようにすればよいのでしょうか?
この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);
}
}
また、"in general"とは、どのような方法が望ましいのでしょうか?
解決方法は?
この行の下にある答えは、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" はパスしてもキャストが失敗する可能性があります。randomObject
の間にある。 -
もし
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
かもしれない のインスタンスである。TargetType
とTargetType
が参照型である場合は、次のようなコードを使用します。TargetType convertedRandomObject = randomObject as TargetType; if (convertedRandomObject != null) { // Do stuff with convertedRandomObject }
-
もし
randomObject
かもしれない のインスタンスである。TargetType
とTargetType
が値型ならas
とTargetType
そのものを使用することはできませんが、NULL可能な型を使用することは可能です。TargetType? convertedRandomObject = randomObject as TargetType?; if (convertedRandomObject != null) { // Do stuff with convertedRandomObject.Value }
(注意:現在、これは よりも実際に遅い + cast . 私はこの方がエレガントで一貫性があると思いますが、どうでしょうか?)
-
変換された値は本当に必要ないが、その値が は のインスタンスである場合、TargetType の
is
演算子はあなたの味方です。この場合、TargetType が参照型であるか値型であるかは問題ではありません。 -
ジェネリックを含む他のケースとして
is
は便利ですが(Tが参照型かどうか分からないのでasは使えない)、比較的不明瞭です。 -
を使ったことがあるのは、ほぼ間違いない。
is
を使うことを思いつかず、今まで値型の場合のas
を併用することです :)
EDIT: Value Typeのケース以外では、nullableのvalue Typeへのunboxingは実際に遅くなることを指摘しましたが、一貫しています。
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が間違いなく有効である状況もあります。 は が遅くなる。もしインターフェイスをチェックするのであれば、バランスは若干as-plus-null-checkに傾くでしょう)。
- それらはすべて めちゃめちゃ が速い。これは単に はしません。 を本当にしないのでなければ、コードのボトルネックになることはないでしょう。 なんでも を、その後の値で指定します。
だから、パフォーマンスを気にするのはやめよう。正しさと一貫性について心配しましょう。
変数を扱う場合、is-and-cast (または is-and-as) はどちらも安全でないと私は主張します。それはかなり稀な状況でしょうが、私はむしろ一貫して使用できる規約を持ちたいと思います。
また、as-then-null-checkは、より良い懸念の分離を与えることを主張します。変換を試みるステートメントと、その結果を使用するステートメントがあります。is-and-castまたはis-and-asはテストを実行し では というように、値の変換を再度試みる。
別の言い方をすれば、誰もが これまで を書きます。
int value;
if (int.TryParse(text, out value))
{
value = int.Parse(text);
// Use value
}
これは、is-and-castがやっていることと同じようなものです--明らかに、もっと安い方法ですが。
関連
-
[解決済み】プログラム実行中に1秒待つ
-
[解決済み] クラス変数に関するアップキャスティングとダウンキャスティングの違いを教えてください。
-
[解決済み] C#のStringとstringの違いは何ですか?
-
[解決済み] C#の正しいバージョン番号を教えてください。
-
[解決済み] mallocの結果はキャストするのですか?
-
[解決済み] usingディレクティブはネームスペースの内側と外側のどちらを使うべきですか?
-
[解決済み] ダイレクトキャスト vs 'as'演算子?
-
[解決済み] Javaでlongをintに安全にキャストする
-
[解決済み】C#のyieldキーワードは何に使われるのか?
-
[解決済み】なぜJavaの+=, -=, *=, /=複合代入演算子はキャスティングを必要としないのですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】"出力タイプがクラスライブラリのプロジェクトは直接起動できない"
-
[解決済み】スクリプトクラスが見つからないので、スクリプトコンポーネントを追加できない?
-
[解決済み】文字列が有効な DateTime " format dd/MM/yyyy " として認識されなかった。
-
[解決済み] エンティティタイプ <type> は、現在のコンテキストのモデルの一部ではありません。
-
[解決済み】C# - パスに不正な文字がある場合
-
[解決済み】Socket.Selectがエラー "An operation was attempted on something that is not a socket" を返す。
-
[解決済み】 C# 条件演算子エラー 代入、call、increment、decrement、await、new object 式のみ文として使用可能です。
-
[解決済み】スレッド終了またはアプリケーションの要求により、I/O操作が中断されました。
-
[解決済み】Nullableオブジェクトは値を持たなければならない?
-
[解決済み] C#の "as "キャストとクラシックキャストの比較 [重複]