Ответ под строкой был написан в 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
}
Это своего рода то, что делает актерский состав - хотя, очевидно, более дешевым способом.