行の下の答えは2008年に書かれました.
as
C# 7 ではパターン マッチングが導入され、次のように記述できるようになり、演算子が大幅に置き換えられました。
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
なく、 がローカル変数ではなくフィールドである場合、別のものをチェックしている可能性があります。randomObject
別のスレッドが2 つの間の値を変更すると、「if」が渡されてもキャストが失敗する可能性があります。
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
性があり、値型である場合、それ自体では使用できませんが、null 許容型を使用できます。TargetType
TargetType
as
TargetType
TargetType? convertedRandomObject = randomObject as TargetType?;
if (convertedRandomObject != null)
{
// Do stuff with convertedRandomObject.Value
}
(注: 現在、これは + cast よりも実際には遅いです。よりエレガントで一貫性があると思いますが、これで終わりです。)
変換された値が本当に必要ではなく、それが TargetType のインスタンスであるかどうかを知る必要がある場合は、演算子is
が役に立ちます。この場合、TargetType が参照型か値型かは問題ではありません。
が便利なジェネリックを含む他のケースがあるis
かもしれません (T が参照型であるかどうかがわからないため、as を使用できないため) が、それらは比較的あいまいです。
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);
}
}
私のラップトップでは、これらすべてが約 60 ミリ秒で実行されます。2 つの注意事項:
- それらの間に大きな違いはありません。(実際には、as-plus-null-check の方が確実に遅い状況があります。上記のコードは、シールされたクラス用であるため、実際には型チェックを簡単にします。インターフェイスをチェックしている場合、バランスがわずかに傾いています。 as-plus-null-check を優先します。)
- それらはすべてめちゃくちゃ速いです。後で値を実際に使用しない場合を除き、これがコードのボトルネックになることはありません。
ですから、パフォーマンスについて心配する必要はありません。正確さと一貫性について心配しましょう。
is-and-cast (または is-and-as) は、参照する値の型がテストとキャストの間の別のスレッドによって変わる可能性があるため、変数を扱う場合はどちらも安全ではないと私は主張します。それは非常にまれな状況ですが、一貫して使用できる規則が必要です。
私はまた、as-then-null-check の方が懸念事項をより適切に分離できると主張しています。変換を試みる 1 つのステートメントと、その結果を使用する 1 つのステートメントがあります。is-and-cast または is-and-as はテストを実行してから、値の変換をもう一度試みます。
別の言い方をすれば、誰でも次のように書いたことがありますか?
int value;
if (int.TryParse(text, out value))
{
value = int.Parse(text);
// Use value
}
それは is-and-cast が行っていることの一種です - 明らかにかなり安価な方法ではありますが。