Рассмотрим следующий код:
void Handler(object o, EventArgs e)
{
// I swear o is a string
string s = (string)o; // 1
//-OR-
string s = o as string; // 2
// -OR-
string s = o.ToString(); // 3
}
В чем разница между тремя типами кастинга (хорошо, третий - это не кастинг, но вы получаете намерение). Какой из них предпочтительнее?
string s = (string)o; // 1
Выбрасывает InvalidCastException, если o
не является string
. В противном случае присваивается o
<<24 > , даже если o
null
.
string s = o as string; // 2
Назначает null
в s
, если o
не является string
, или если o
- null
. По этой причине вы не можете использовать его со значениями типов (оператор в таком случае никогда не сможет вернуть null
). В противном случае присваивается o
s
.
string s = o.ToString(); // 3
Вызывает NullReferenceException, если o
- null
. Назначает все o.ToString()
возвращается к s
, независимо от типа o
.
Используйте для большинства конверсий 1 - это просто и понятно. Я почти никогда не использую 2, поскольку, если что-то не соответствует типу, я обычно ожидаю, что произойдет исключение. Я только видел потребность в этом возврате-нулевом типе функциональности с плохо разработанными библиотеками, использующими коды ошибок (например, return null = error, вместо использования исключений).
3 не является литой и является просто вызовом метода. Используйте его, когда вам нужно строковое представление нестрокового объекта.
Это действительно зависит от того, знаете ли вы, что o
- это строка и что вы хотите с ней делать. Если ваш комментарий означает, что o
действительно действительно является строкой, я бы предпочел прямое выполнение (string)o
- он вряд ли потерпит неудачу.
Самое большое преимущество использования прямой трансляции заключается в том, что когда она терпит неудачу, вы получаете InvalidCastException, в котором говорится о многом неправильно.
С помощью оператора as
, если o
не является строкой, s
устанавливается в null
, что удобно, если вы не уверены и хотите протестировать s
:
string s = o as string;
if ( s == null )
{
// well that not good!
gotoPlanB();
}
Однако, если вы не выполните этот тест, вы будете использовать s
позже и создадите NullReferenceException. Они, как правило, более распространены и намного сложнее отслеживать, когда они происходят в дикой природе, так как почти каждая строка разыгрывает переменную и может ее выбросить. С другой стороны, если вы пытаетесь применить тип значения (любой примитив или структуры, такие как DateTime), у вас есть для использования прямого литья - as
не будет работать.
В специальном случае преобразования в строку каждый объект имеет ToString
, поэтому ваш третий метод может быть в порядке, если o
не является нулевым, и вы думаете, что метод ToString
может делать то, что вы хотите.
as
с обнуляемыми типами значений. IE o as DateTime
не будет работать, но o as DateTime?
будут...
if (s is string)
?
Если вы уже знаете, к какому типу он может быть применен, используйте C-стиль:
var o = (string) iKnowThisIsAString;
Обратите внимание, что только при использовании C-стиля вы можете выполнять явное принуждение типа.
Если вы не знаете, является ли он желаемым типом, и вы собираетесь использовать его, если это так, используйте как ключевое слово:
var s = o as string;
if (s != null) return s.Replace("_","-");
//or for early return:
if (s==null) return;
Обратите внимание, что как не будет вызывать операторов преобразования типов. Он будет только не null, если объект не является нулевым и изначально указанного типа.
Используйте ToString(), чтобы получить удобочитаемое строковое представление любого объекта, даже если оно не может быть передано в строку.
Ключевое слово as в asp.net используется хорошо, когда вы используете метод FindControl.
Hyperlink link = this.FindControl("linkid") as Hyperlink;
if (link != null)
{
...
}
Это означает, что вы можете работать с типизированной переменной, а затем затем отбрасывать ее из object
, как и при прямом нажатии:
object linkObj = this.FindControl("linkid");
if (link != null)
{
Hyperlink link = (Hyperlink)linkObj;
}
Это не огромная вещь, но она сохраняет строки кода и назначение переменных, а также более читабельную
'as' основан на 'is', который является ключевым словом, который проверяет во время выполнения, если объект является полиморфно совместимым (в основном, если акты могут быть сделаны) и возвращает null, если проверка завершилась неудачей.
Эти два эквивалента:
Использование 'as':
string s = o as string;
Использование 'is':
if(o is string)
s = o;
else
s = null;
Напротив, приведение в стиле c выполняется также во время выполнения, но генерирует исключение, если приведение не может быть выполнено.
Просто добавьте важный факт:
Ключевое слово 'as' работает только со ссылочными типами. Вы не можете:
// I swear i is an int
int number = i as int;
В этих случаях вы должны использовать кастинг.
2 полезен для литья производного типа.
Предположим, что a является Animal:
b = a as Badger;
c = a as Cow;
if (b != null)
b.EatSnails();
else if (c != null)
c.EatGrass();
получит a с минимальным количеством бросков.
"(string) o" приведет к InvalidCastException, поскольку нет прямого приведения.
"o как строка" приведет к тому, что s будет пустой ссылкой, а не генерируется исключение.
"o.ToString()" не является литой какого-либо рода per se, это метод, реализованный объектом и, таким образом, так или иначе, каждым классом в .net, который "делает что-то" с экземпляр класса, который он вызывал, и возвращает строку.
Не забывайте, что для преобразования в строку также используется Convert.ToString(someType instanceOfThatType), где someType является одним из множества типов, по существу базовых типов фреймворков.
Кажется, что оба они концептуально отличаются.
Прямое литье
Типы не обязательно должны быть строго связаны. Он поставляется во всех видах ароматов.
Похоже, что объект будет преобразован во что-то другое.
Оператор AS
Типы имеют прямую связь. Как в:
Похоже, вы собираетесь обрабатывать объект по-другому.
Образцы и IL
class TypeA
{
public int value;
}
class TypeB
{
public int number;
public static explicit operator TypeB(TypeA v)
{
return new TypeB() { number = v.value };
}
}
class TypeC : TypeB { }
interface IFoo { }
class TypeD : TypeA, IFoo { }
void Run()
{
TypeA customTypeA = new TypeD() { value = 10 };
long longValue = long.MaxValue;
int intValue = int.MaxValue;
// Casting
TypeB typeB = (TypeB)customTypeA; // custom explicit casting -- IL: call class ConsoleApp1.Program/TypeB ConsoleApp1.Program/TypeB::op_Explicit(class ConsoleApp1.Program/TypeA)
IFoo foo = (IFoo)customTypeA; // is-a reference -- IL: castclass ConsoleApp1.Program/IFoo
int loseValue = (int)longValue; // explicit -- IL: conv.i4
long dontLose = intValue; // implict -- IL: conv.i8
// AS
int? wraps = intValue as int?; // nullable wrapper -- IL: call instance void valuetype [System.Runtime]System.Nullable`1<int32>::.ctor(!0)
object o1 = intValue as object; // box -- IL: box [System.Runtime]System.Int32
TypeD d1 = customTypeA as TypeD; // reference conversion -- IL: isinst ConsoleApp1.Program/TypeD
IFoo f1 = customTypeA as IFoo; // reference conversion -- IL: isinst ConsoleApp1.Program/IFoo
//TypeC d = customTypeA as TypeC; // wouldn't compile
}
Все полученные ответы хороши, если я могу что-то добавить: Чтобы напрямую использовать строковые методы и свойства (например, ToLower), вы не можете писать:
(string)o.ToLower(); // won't compile
вы можете писать только:
((string)o).ToLower();
но вместо этого вы можете написать:
(o as string).ToLower();
Опция as
более читаема (по крайней мере, на мой взгляд).
(o as string).ToLower()
вместо нескольких запутанных скобок.
В соответствии с экспериментами, запущенными на этой странице: http://www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as
(на этой странице иногда появляются ошибки "незаконного реферера", поэтому просто обновляйте, если это произойдет)
Вывод: оператор "как" обычно быстрее, чем приведение. Иногда во много раз быстрее, иногда просто быстрее.
Я воспринимаю нечто более "понятное".
Итак, поскольку он работает быстрее и "безопаснее" (wont throw exception), и, возможно, его легче читать, я рекомендую использовать "как" все время.
string s = o as string; // 2
Предпочитается, поскольку это позволяет избежать штрафа за производительность при двойном литье.
В С# поддерживаются следующие две формы преобразования типов (литье):
|
(Резюме
• Преобразование статического типа v в c в данном выражении
• возможно только в том случае, если динамический тип v является c или подтипом c
• Если нет, исключается InvalidCastException
|
v как C
• Нефатальный вариант (c) v
• Таким образом, преобразуйте статический тип v в c в данном выражении
• Возвращает значение null, если динамический тип v не является c, или подтип c
Использовать прямой прилив string s = (string) o;
, если в логическом контексте вашего приложения string
является единственным допустимым типом. При таком подходе вы получите InvalidCastException
и реализуете принцип Fail-fast. Ваша логика будет защищена от дальнейшей передачи недопустимого типа или получения исключения NullReferenceException, если используется оператор as
.
Если логика ожидает несколько разных типов, произведите string s = o as string;
и проверьте ее на null
или используйте оператор is
.
В С# 7.0 появилась новая крутая функция для упрощения приведения и проверки Соответствие шаблону:
if(o is string s)
{
// Use string variable s
}
or
switch (o)
{
case int i:
// Use int variable i
break;
case string s:
// Use string variable s
break;
}
Так как никто не упомянул об этом, ближайший к instanceOf к Java по ключевым словам:
obj.GetType().IsInstanceOfType(otherObj)
При попытке получить строковое представление чего-либо (любого типа), потенциально имеющего нуль, я предпочитаю следующую строку кода. Он компактен, он вызывает ToString(), и он корректно обрабатывает нули. Если o равно null, s будет содержать String.Empty.
String s = String.Concat(o);
string s = Convert.ToString(o)
; 5-е:string s = $"{o}"
(или эквивалентноstring.Format
Формаstring.Format
для более раннего C #)