Câu trả lời dưới đây được viết vào năm 2008.
Kết hợp mẫu giới thiệu C # 7, phần lớn đã thay thế as
toán tử, như bây giờ bạn có thể viết:
if (randomObject is TargetType tt)
{
// Use tt here
}
Lưu ý rằng tt
vẫn còn trong phạm vi sau này, nhưng không được chỉ định chắc chắn. (Đó là chắc chắn giao trong if
cơ thể.) Đó là một chút khó chịu trong một số trường hợp, vì vậy nếu bạn thực sự quan tâm về việc giới thiệu số lượng nhỏ nhất của các biến thể trong tất cả các phạm vi, bạn vẫn có thể muốn sử dụng is
tiếp theo là một diễn viên.
Tôi không nghĩ bất kỳ câu trả lời nào cho đến nay (tại thời điểm bắt đầu câu trả lời này!) Đã thực sự giải thích nơi nào đáng để sử dụng.
Đừng làm điều này:
// Bad code - checks type twice for no reason
if (randomObject is TargetType)
{
TargetType foo = (TargetType) randomObject;
// Do something with foo
}
Điều này không chỉ kiểm tra hai lần, mà còn có thể kiểm tra những thứ khác nhau, nếu randomObject
là một trường chứ không phải là một biến cục bộ. Có thể cho "nếu" vượt qua nhưng sau đó diễn viên thất bại, nếu một luồng khác thay đổi giá trị randomObject
giữa hai.
Nếu randomObject
thực sự nên là một ví dụ TargetType
, nghĩa là nếu không, điều đó có nghĩa là có lỗi, thì việc truyền là một giải pháp phù hợp. Điều đó ném ra một ngoại lệ ngay lập tức, điều đó có nghĩa là không có thêm công việc nào được thực hiện theo các giả định không chính xác và ngoại lệ hiển thị chính xác loại lỗi.
// 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;
Nếu randomObject
có thể là một thể hiện của TargetType
và TargetType
là một loại tham chiếu, thì hãy sử dụng mã như thế này:
TargetType convertedRandomObject = randomObject as TargetType;
if (convertedRandomObject != null)
{
// Do stuff with convertedRandomObject
}
Nếu randomObject
có thể là một thể hiện của TargetType
và TargetType
là một loại giá trị, thì chúng ta không thể sử dụng as
với TargetType
chính nó, nhưng chúng ta có thể sử dụng một loại nullable:
TargetType? convertedRandomObject = randomObject as TargetType?;
if (convertedRandomObject != null)
{
// Do stuff with convertedRandomObject.Value
}
(Lưu ý: hiện tại điều này thực sự chậm hơn so với + diễn viên . Tôi nghĩ rằng nó thanh lịch và nhất quán hơn, nhưng chúng ta sẽ đi.)
Nếu bạn thực sự không cần giá trị được chuyển đổi, nhưng bạn chỉ cần biết liệu đó có phải là phiên bản của TargetType hay không, thì is
toán tử là bạn của bạn. Trong trường hợp này, không quan trọng việc TargetType là loại tham chiếu hay loại giá trị.
Có thể có các trường hợp khác liên quan đến thuốc generic is
rất hữu ích (vì bạn có thể không biết liệu T có phải là loại tham chiếu hay không, vì vậy bạn không thể sử dụng như) nhưng chúng tương đối mù mờ.
Tôi gần như chắc chắn đã sử dụng is
cho trường hợp loại giá trị trước đây, không nghĩ đến việc sử dụng loại nullable và as
cùng nhau :)
EDIT: Lưu ý rằng không có điều nào ở trên nói về hiệu suất, ngoại trừ trường hợp loại giá trị, trong đó tôi đã lưu ý rằng việc hủy hộp đến loại giá trị null thực sự chậm hơn - nhưng nhất quán.
Theo câu trả lời của naasking, is-and-cast hoặc is-and-as đều nhanh như kiểm tra bằng và không null với các JIT hiện đại, như được hiển thị theo mã dưới đây:
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);
}
}
Trên máy tính xách tay của tôi, tất cả thực hiện trong khoảng 60ms. Hai điều cần lưu ý:
- Không có sự khác biệt đáng kể giữa chúng. (Trong thực tế, có những tình huống trong đó như-cộng-null-kiểm tra chắc chắn là chậm Đoạn mã trên thực sự làm cho các loại kiểm tra dễ dàng bởi vì nó cho một lớp niêm phong;. Nếu bạn kiểm tra đang cho một giao diện, những lời khuyên cân bằng hơi ủng hộ as-plus-null-check.)
- Tất cả đều cực kỳ nhanh. Điều này chỉ đơn giản sẽ không là nút cổ chai trong mã của bạn trừ khi bạn thực sự sẽ không làm gì với các giá trị sau đó.
Vì vậy, đừng lo lắng về hiệu suất. Hãy lo lắng về tính đúng đắn và nhất quán.
Tôi duy trì rằng cả-và-cast (hoặc is-and-as) đều không an toàn khi xử lý các biến, vì loại giá trị mà nó đề cập có thể thay đổi do một luồng khác giữa phép thử và phép đúc. Đó sẽ là một tình huống khá hiếm gặp - nhưng tôi muốn có một quy ước mà tôi có thể sử dụng một cách nhất quán.
Tôi cũng duy trì rằng kiểm tra as-then-null giúp phân tách mối quan tâm tốt hơn. Chúng tôi có một tuyên bố cố gắng chuyển đổi và sau đó một tuyên bố sử dụng kết quả. Is-and-cast hoặc is-and-as thực hiện một thử nghiệm và sau đó một nỗ lực khác để chuyển đổi giá trị.
Nói một cách khác, sẽ có ai từng viết:
int value;
if (int.TryParse(text, out value))
{
value = int.Parse(text);
// Use value
}
Đó là loại những gì đang diễn ra - mặc dù rõ ràng là theo một cách khá rẻ hơn.