Рассмотрим следующий код:
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
}
В чем разница между тремя типами кастинга (ладно, третий - не кастинг, но вы понимаете намерение). Какой из них предпочесть?
4-й: string s = Convert.ToString(o); 5-й: string s = $"{o}" (или эквивалентная форма string.Format для более раннего C#)





'as' основано на 'is', которое является ключевым словом, которое проверяет во время выполнения, является ли объект полиморфно совместимым (в основном, если может быть выполнено приведение), и возвращает null, если проверка не удалась.
Эти два эквивалента:
Использование "как":
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;
В этих случаях вам нужно использовать кастинг.
Спасибо, что указали на мою ошибку, вы правы. Я отредактировал ответ. ой, извините.
string s = (string)o; // 1
Выбрасывает InvalidCastException, если o не является string. В противном случае присваивает os, даже если o - null.
string s = o as string; // 2
Присваивает null к s, если o не является string или если o является null. По этой причине вы не можете использовать его с типами значений (в этом случае оператор никогда не сможет вернуть null). В противном случае присваивает os.
string s = o.ToString(); // 3
Вызывает NullReferenceException, если o - null. Назначает все, что o.ToString() возвращает s, независимо от типа o.
Используйте 1 для большинства конверсий - это просто и понятно. Я почти никогда не использую 2, поскольку, если что-то не того типа, я обычно ожидаю возникновения исключения. Я видел потребность в функциональности этого типа return-null только с плохо спроектированными библиотеками, которые используют коды ошибок (например, return null = error вместо использования исключений).
3 - это не приведение, а просто вызов метода. Используйте его, когда вам нужно строковое представление нестрокового объекта.
Вы можете присвоить значение null типам, если они явно определены, например: int? я; строка s = "5"; я = s как int; // i теперь 5 s = null; я = s как int; // теперь я равен нулю
RE: Anheledir На самом деле после первого звонка я был бы нулевым. Вы должны использовать явную функцию преобразования, чтобы получить значение строки.
RE: Sander На самом деле есть еще одна очень веская причина для использования as, это упрощает ваш проверочный код (проверяйте на null, а не проверяйте на null и правильный тип). Это полезно, поскольку в большинстве случаев вы предпочитаете генерировать индивидуальное исключение. Но это правда, что слепые как коллы - это плохо.
# 2 удобен для таких вещей, как методы Equals, для которых вы не знаете тип ввода. В целом, да, предпочтительнее 1. Хотя предпочтительнее, чем это, очевидно, было бы использование системы типов для ограничения одним типом, когда вы ожидаете только одного :)
№2 также полезен, когда у вас есть код, который может делать что-то определенное для специализированного типа, но в противном случае ничего не делал бы.
Как общее практическое правило. Использование as должно сопровождаться тестом, чтобы убедиться, что это null. Или, по крайней мере, другой код, который принимает null в качестве допустимого ввода. Тем не менее, я довольно часто использую преобразование as, пример, в котором я бы использовал его, - это метод, который принимает IEnumerable<T>, но может предварительно выделить коллекцию динамического размера, если это также ICollection. ICollection col = input as ICollection; if (col != null) something.Reserve(col.Count);
Это действительно зависит от того, знаете ли вы, является ли o строкой, и что вы хотите с ней делать. Если ваш комментарий означает, что o действительно является строкой, я бы предпочел прямое приведение (string)o - вряд ли он потерпит неудачу.
Самым большим преимуществом использования прямого преобразования является то, что в случае сбоя вы получаете InvalidCastException, который в значительной степени сообщает вам, что пошло не так.
С оператором as, если o не является строкой, s устанавливается на null, что удобно, если вы не уверены и хотите протестировать s:
string s = o as string;
if ( s == null )
{
// well that's not good!
gotoPlanB();
}
Однако, если вы не выполните этот тест, вы будете использовать s позже и получите NullReferenceException. Они имеют тенденцию быть более распространенными, и много труднее отследить, если они появляются в дикой природе, поскольку почти каждая строка разыменовывает переменную и может ее выбросить. С другой стороны, если вы пытаетесь привести к типу значения (любому примитиву или структурам, таким как DateTime), вам нужно использовать прямое приведение - as не будет работать.
В особом случае преобразования в строку каждый объект имеет ToString, поэтому ваш третий метод может подойти, если o не равен нулю и вы думаете, что метод ToString может делать то, что вы хотите.
Одно замечание - вы можете использовать as с типами значений обнуляемый. I.E. o as DateTime не будет работать, но o as DateTime? будет ...
Почему бы вместо этого не использовать if (s is string)?
@BornToCode, для меня во многом личные предпочтения. В зависимости от того, что вы делаете, часто после ising, вам все равно придется разыграть снова, так что у вас есть готовность, а затем жесткое приведение. По какой-то причине as и нулевая проверка мне понравились.
"(string) o" приведет к InvalidCastException, так как прямого приведения нет.
"o as string" приведет к тому, что s будет пустой ссылкой, а не будет выброшено исключение.
«o.ToString ()» не является преобразованием какого-либо вида по сути, это метод, который реализуется объектом, и, таким образом, так или иначе, каждым классом в .net, который «что-то делает» с экземпляром класс, который он вызывает, и возвращает строку.
Не забывайте, что для преобразования в строку существует также Convert.ToString (someType instanceOfThatType), где someType является одним из набора типов, по сути, базовых типов фреймворка.
string s = (string)o; Используйте, когда что-то нужно
определенно быть другим.string s = o as string; Используется, когда что-то возможно другое
предмет.string s = o.ToString(); Используйте, когда вам все равно, что
это так, но вы просто хотите использовать
доступное строковое представление.Я чувствую, что этот ответ звучит хорошо, но может быть неточным.
Мне нравятся первые два, но я бы добавил к третьему варианту «и вы уверены, что это не ноль».
в наши дни вы можете использовать Элвиса (?.), чтобы не заботиться об этом: obj? .ToString ()
@Quibblesome хороший ответ: вас не раздражает, если я добавлю, что такое 1/2/3, чтобы не нужно было прокручивать вверх до OP. Я бы с ТАК ранжировал старые ответы по голосам!
2 полезен для приведения к производному типу.
Предположим, что а - животное:
b = a as Badger;
c = a as Cow;
if (b != null)
b.EatSnails();
else if (c != null)
c.EatGrass();
получит а с минимальным количеством бросков.
@Chirs Moutray, это не всегда возможно, особенно если это библиотека.
Если вы уже знаете, к какому типу он может приводить, используйте приведение в стиле 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 только в том случае, если объект не является NULL и изначально относится к указанному типу.
Используйте ToString (), чтобы получить удобочитаемое строковое представление любого объекта, даже если он не может быть преобразован в строку.
Это небольшая интересная проблема, касающаяся операторов преобразования типов. У меня есть несколько типов, для которых я создал конверсии, тогда я должен остерегаться этого.
string s = o as string; // 2
Рекомендуется, так как позволяет избежать потери производительности от двойного преобразования.
Привет, Крис, ссылка, которая была в этом ответе, теперь 404 ... Я не уверен, есть ли у вас замена, которую вы хотите вставить на ее место?
Ключевое слово 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;
}
Это небольшая вещь, но она позволяет сэкономить строки кода и присваивать переменные, а также более удобочитаема.
Согласно экспериментам, проведенным на этой странице: http://www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as
(на этой странице иногда появляются некоторые ошибки "нелегального реферера", поэтому просто обновите, если они есть)
Вывод: оператор «as» обычно выполняется быстрее, чем приведение типов. Иногда во много раз быстрее, иногда чуть быстрее.
Я лично считаю «как» и более читабельным.
Итак, поскольку он и быстрее, и «безопаснее» (не вызывает исключений) и, возможно, его легче читать, я рекомендую все время использовать «как».
При попытке получить строковое представление чего-либо (любого типа), которое потенциально может иметь значение NULL, я предпочитаю следующую строку кода. Он компактен, вызывает ToString () и правильно обрабатывает значения NULL. Если o равно нулю, s будет содержать String.Empty.
String s = String.Concat(o);
Все полученные ответы хороши, если можно что-то добавить: Чтобы напрямую использовать строковые методы и свойства (например, ToLower), вы не можете написать:
(string)o.ToLower(); // won't compile
можно только написать:
((string)o).ToLower();
но вместо этого вы можете написать:
(o as string).ToLower();
Вариант as более читабелен (по крайней мере, на мой взгляд).
конструкция (o as string) .ToLower () отменяет назначение оператора as. Это вызовет исключение нулевой ссылки, когда o не может быть преобразовано в строку.
@james - Но кто сказал, что единственная цель оператора as - генерировать исключение в случае сбоя приведения? Если вы знать, что o является строкой, и просто хотите написать более чистый код, вы можете использовать (o as string).ToLower() вместо нескольких сбивающих с толку скобок.
цель as прямо противоположна - он не должен вызывать исключение при сбое преобразования, он должен возвращать значение null. Допустим, ваш o - это строка со значением null, что тогда произойдет? Подсказка - ваш вызов ToLower завершится неудачно.
@james - Вы правы, но как насчет случаев, когда я точно знаю, что он не будет нулевым, и мне просто нужно выполнить приведение для компилятора, чтобы позволить мне получить доступ к методам этого объекта?
вы определенно можете это сделать, но это не совсем лучшая практика, потому что вы не хотите полагаться на вызывающую или внешние системы, чтобы убедиться, что ваше значение не равно нулю. Если вы используете C# 6, вы могли бы сделать (o как строку) ?. Понижать().
Поскольку никто не упомянул об этом, наиболее близким к instanceOf к Java по ключевому слову является следующее:
obj.GetType().IsInstanceOfType(otherObj)
Похоже, они концептуально разные.
Прямая трансляция
Типы не обязательно должны быть строго связаны. Он бывает со всеми типами вкусов.
Такое ощущение, что объект собирается превратиться во что-то другое.
Оператор AS
Типы имеют прямое отношение. Как в:
Такое ощущение, что вы собираетесь обращаться с объектом по-другому.
Образцы и ИЖ
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
}
Используйте прямое приведение string s = (string) o;, если в логическом контексте вашего приложения string является единственным допустимым типом. При таком подходе вы получите InvalidCastException и реализуете принцип Быстро. Ваша логика будет защищена от дальнейшей передачи недопустимого типа или получения исключения NullReferenceException, если используется оператор as.
Если логика предполагает несколько разных типов, передайте string s = o as string; и проверьте его на null или используйте оператор is.
В C# 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;
}
Хочу обратить внимание на следующие особенности оператора как:
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/as
Note that the as operator performs only reference conversions, nullable conversions, and boxing conversions. The as operator can't perform other conversions, such as user-defined conversions, which should instead be performed by using cast expressions.
Не совсем дубликат, но есть также некоторые обсуждения производительности в предыдущий вопрос.