Ответ под строкой был написан в 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
}
Эта проверка не только повторяется дважды, но и может проверять разные вещи, если randomObject
является полем, а не локальной переменной. Возможно, что «если» пройдет, но тогда приведение не будет выполнено, если другой поток изменит значение 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
, но мы можно использовать обнуляемый тип:
TargetType? convertedRandomObject = randomObject as TargetType?;
if (convertedRandomObject != null)
{
// Do stuff with convertedRandomObject.Value
}
(Примечание: в настоящее время это на самом деле медленнее, чем + + . Я думаю, что это более элегантно и последовательно, но мы идем.)
Если вам действительно не нужно преобразованное значение, но вам просто нужно знать, является ли оно экземпляром TargetType, тогда оператор is
- ваш друг. В этом случае не имеет значения, является ли TargetType ссылочным типом или типом значения.
В других случаях, связанных с генериками, могут быть полезны is
(потому что вы можете не знать, является ли T ссылочным типом или нет, поэтому вы не можете использовать как), но они относительно неясны.
Я почти наверняка использовал is
для случая типа значения до сих пор, не задумываясь об использовании обнуляемого типа и as
вместе:)
РЕДАКТИРОВАТЬ: обратите внимание, что ни один из вышеперечисленных не говорит о производительности, кроме случая типа значения, где я отметил, что распаковка в тип значения, допускающий значение NULL, на самом деле медленнее - но согласованно.
В соответствии с ответом Нааскинга, is-and-cast или is-and-as являются такими же быстрыми, как и нулевая проверка с современными JIT, как показано кодом ниже:
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 мс. Следует отметить две вещи:
- Между ними нет существенной разницы. (На самом деле, бывают ситуации, когда проверка as-plus-null определенно медленнее . Приведенный выше код на самом деле облегчает проверку типа, потому что это для запечатанного класса; если вы проверяете для интерфейс, баланс немного склоняется в пользу проверки как плюс-ноль.)
- Они все безумно быстро. Это просто не будет узким местом в вашем коде, если вы действительно не собираетесь делать что-нибудь со значениями впоследствии.
Так что давайте не будем беспокоиться о производительности. Давайте позаботимся о правильности и последовательности.
Я утверждаю, что is-and-cast (или is-and-as) оба небезопасны при работе с переменными, поскольку тип значения, на которое он ссылается, может измениться из-за другого потока между тестом и приведением. Это было бы довольно редкой ситуацией - но я бы предпочел соглашение, которое я мог бы использовать последовательно.
Я также утверждаю, что проверка "как тогда ноль" дает лучшее разделение проблем. У нас есть одно утверждение, которое пытается преобразовать, а затем одно утверждение, которое использует результат. Метод is-and-cast или is-and-as выполняет проверку, а затем еще одну попытку преобразовать значение.
Другими словами, может ли кто-нибудь когда-либо написать:
int value;
if (int.TryParse(text, out value))
{
value = int.Parse(text);
// Use value
}
Это то, что делает актерский состав и актерский состав - хотя, очевидно, более дешевым способом.