Начиная с C # 6 (июль 2015 г.), предполагая, что Node
- это class
(или Nullable<T>
, string
и т. Д.), Используя ваш пример, где вы
- проверьте, является ли
o
Node
(на самом деле не то же самое, что преобразование o
в a Node
- см. Примечание о приведении к преобразованию ниже)
- если это так, звоните
do_it()
- немедленно отбрасывает приведенное значение
Вы можете использовать нулевой условный оператор :
(o as Node)?.do_it();
Этот синтаксис также обрабатывает случай, когда o
фактически объявлен как Node
, но бывает null
.
Если вы хотите сохранить переменную приведения, начиная с C # 7 (март 2017 г.), вы можете запустить:
if (o is Node node)
{
node.do_it();
}
Переменная node
на данный момент находится в области действия вне оператора if
, что эквивалентно:
Node node = o as Node;
if (node != null)
{
node.do_it();
}
Итак, если вы хотите продолжить выполнение , если o
равно Node
, вы можете написать:
if (!(o is Node node))
{
return; // or continue, etc
}
node.do_it();
// ...
Примечание. Ключевое слово is
будет всегда возвращать false
, если o
равно null
, даже если вы прямо указали тип, а затем спросите, является ли эта переменная этим типом.
string foo = null;
if (foo is string)
{
// never gets here
Console.WriteLine(foo);
}
Кастинг против Конвертации
Ключевые слова is
и as
делают то же самое, что и dynamic_cast<T>
в C ++: они проверяют указанный тип, подтип или интерфейс, но фактически не изменяют значение в памяти. Они просто сообщают компилятору, какие методы должны быть доступны для переменной.
Среди пользователей C # есть неправильное выражение: мы используем слова «cast» и «convert» взаимозаменяемо. Вероятно, это связано с тем, что мы часто знаем , что переменная базового типа всегда будет подтипом, и поэтому мы используем синтаксис convert , когда по-пуритански нам следует использовать приведение синтаксис:
void Foo(MyBaseType value)
{
// let's assume `value` will always be a MySubType
MySubType subTypeValue = (MySubType)value;
}
Этот синтаксис генерирует во время выполнения, если value
на самом деле не MySubType
.
Преобразование отличается от преобразования тем, что значение в памяти может измениться. Рассмотрим int
и double
.
void Foo()
{
// implicit converting
int x = 1;
double y = x;
// explicit converting
y = 1.5;
x = (int)y;
}
В каждом из этих случаев литеральное значение, хранящееся в памяти, изменяет формат. int
s всегда можно представить как double
- данные никогда не будут потеряны - и поэтому существует определенный implicit operator
, который будет манипулировать данными в памяти в новом формате. double
s, будучи значениями с плавающей запятой и имеющими диапазон больше int
s, не может гарантировать отсутствие потери данных, поэтому C # требует явного преобразования (обычно называемого «явным приведением») через explicit operator
, чтобы указать компилятор, что мы в порядке с потерей данных.
С помощью классов мы можем определить наши собственные неявные и явные операторы, которые будут манипулировать данными любым удобным для нас способом. Это где неправильное число между преобразовать и приведение становится грязным.
using System;
public class Program
{
public static void Main()
{
Foo foo = new Foo();
Bar bar = (Bar)foo;
// writes "1" (or would, if the program compiled)
Console.WriteLine(bar);
// throws compilation error: "Cannot convert type `Foo' to `Bar' via built-in conversion"
bar = foo as Bar;
// note: the same would happen if `foo` was type int? and `bar` was type `double?`
// even though double? can be converted to int?
}
}
class Foo
{
public readonly int Value = 1;
public static explicit operator Bar(Foo foo)
{
return new Bar(foo.Value.ToString());
}
}
class Bar
{
public readonly string Value;
public Bar(string value)
{
Value = value;
}
}