Ответ под строкой был написан в 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
}
(Примечание: в настоящее время это на самом деле медленнее, чем + + cast . Я думаю, что это более элегантно и последовательно, но мы здесь.)
Если вам действительно не нужно преобразованное значение, но вам просто нужно знать, является ли оно экземпляром 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 мс. Обратите внимание на две вещи:
- Там нет значительной разницы между ними. (На самом деле, есть ситуации , в которых , как плюс нуль-проверка , безусловно , является медленнее , приведенный выше код на самом деле делает проверку типа легко , потому что это для герметичного класса;. , Если вы проверяете для интерфейса, балансовые советы немного в пользу как-плюс-ноль-чек.)
- Они все безумно быстрые. Это просто не будет узким местом в вашем коде, если вы действительно не собираетесь ничего делать со значениями впоследствии.
Так что давайте не будем беспокоиться о производительности. Давайте позаботимся о правильности и последовательности.
Я утверждаю, что 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
}
Это своего рода то, что делает актерский состав - хотя, очевидно, более дешевым способом.