Ответ ниже строки был записан в 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
самостоятельно, но мы можем использовать nullable тип:
TargetType? convertedRandomObject = randomObject as TargetType?;
if (convertedRandomObject != null)
{
// Do stuff with convertedRandomObject.Value
}
(Примечание: в настоящее время это на самом деле медленнее, чем + бросок. Я думаю, что это более изящно и последовательно, но там мы идем.)
Если Вам действительно не нужно преобразованное значение, но просто необходимо знать, является ли это экземпляром TargetType, то is
оператор является Вашим другом. В этом случае не имеет значения, является ли TargetType ссылочным типом или типом значения.
Могут быть другие случаи, включающие дженерики где is
полезно (потому что Вы не можете знать, является ли T ссылочным типом или нет, таким образом, Вы не можете использовать в качестве), но они относительно неясны.
Я почти наверняка использовал is
поскольку значение вводит случай до настоящего времени, не думая об использовании nullable типа и as
вместе :)
Править: Обратите внимание, что ни одни из вышеупомянутых переговоров о производительности, кроме значения не вводят случай, где я отметил, что распаковывание к nullable типу значения на самом деле медленнее - но последовательно.
Согласно ответу naasking,-и-бросать или, и как оба с такой скоростью, как 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 мс. Две вещи отметить:
Поэтому давайте не волноваться о производительности. Давайте волноваться о правильности и непротиворечивости.
Я поддерживаю, что-и-бросать (или и как) оба небезопасны при контакте с переменными, поскольку тип значения, к которому он относится, может измениться из-за другого потока между тестом и броском. Это было бы довольно редкой ситуацией - но у меня скорее будет конвенция, которую я могу последовательно использовать.
Я также поддерживаю, что as-then-null-check дает лучшее разделение проблем. У нас есть один оператор, который делает попытку преобразования и затем одного оператора, который использует результат.-И-бросать или и как выполняет тест и затем другую попытку преобразовать значение.
Другими словами, был бы любой когда-либо писать:
int value;
if (int.TryParse(text, out value))
{
value = int.Parse(text);
// Use value
}
Это - вид того, что-и-бросать делает - хотя, очевидно, скорее более дешевым способом.
Мои начиная с большей части количества или частей не требуют десятичного числа, эта функция только покажет десятичное число при необходимости.
str_replace(".00", "", number_format($this->pieces, 2));