Прямое приведение против оператора "как"?

563

Рассмотрим следующий код:

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
}

В чем разница между тремя типами кастинга (хорошо, третий - это не кастинг, но вы получаете намерение). Какой из них предпочтительнее?

  • 1
    Не совсем дубликат, но есть также некоторые обсуждения производительности в предыдущем вопросе .
  • 7
    4-е: string s = Convert.ToString(o) ; 5-е: string s = $"{o}" (или эквивалентно string.Format Форма string.Format для более раннего C #)
Теги:
casting

16 ответов

711
Лучший ответ
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 не является литой и является просто вызовом метода. Используйте его, когда вам нужно строковое представление нестрокового объекта.

  • 2
    Вы можете назначить 'null' для значений-типов, когда они явно определены, например: int? я; строка s = "5"; i = s как int; // теперь у меня 5 s = null; i = s как int; // теперь я нулевой
  • 3
    RE: Anheledir На самом деле я был бы нулевым после первого звонка. Вы должны использовать явную функцию преобразования, чтобы получить значение строки.
Показать ещё 10 комментариев
303
  • Используйте, когда что-то должно определенно быть другим.
  • Используйте, когда что-то может быть другим вещь.
  • Используйте, когда вам все равно, что но вы просто хотите использовать доступное представление строки.
  • 44
    Это отличное дополнение к принятому ответу.
  • 0
    Я чувствую, что этот ответ звучит хорошо, но он может быть неточным.
Показать ещё 5 комментариев
27

Это действительно зависит от того, знаете ли вы, что 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 может делать то, что вы хотите.

  • 2
    Одно замечание - вы можете использовать as с обнуляемыми типами значений. IE o as DateTime не будет работать, но o as DateTime? будут...
  • 0
    Почему бы не использовать if (s is string) ?
Показать ещё 1 комментарий
8

Если вы уже знаете, к какому типу он может быть применен, используйте 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(), чтобы получить удобочитаемое строковое представление любого объекта, даже если оно не может быть передано в строку.

  • 3
    Это интересный небольшой вопрос, касающийся операторов преобразования типов. У меня есть несколько типов, для которых я создал конверсии, поэтому должен следить за этим.
7

Ключевое слово 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;
}

Это не огромная вещь, но она сохраняет строки кода и назначение переменных, а также более читабельную

6

'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;

В этих случаях вы должны использовать кастинг.

  • 0
    'is' работает на любом типе
  • 0
    Спасибо, что указали на мою ошибку, вы правы. Я отредактировал ответ. ой, извините.
5

2 полезен для литья производного типа.

Предположим, что a является Animal:

b = a as Badger;
c = a as Cow;

if (b != null)
   b.EatSnails();
else if (c != null)
   c.EatGrass();

получит a с минимальным количеством бросков.

  • 1
    Но было бы лучше использовать полиморфизм для этого типа проблемы ...
  • 2
    @Chirs Moutray, это не всегда возможно, особенно если это библиотека.
4

"(string) o" приведет к InvalidCastException, поскольку нет прямого приведения.

"o как строка" приведет к тому, что s будет пустой ссылкой, а не генерируется исключение.

"o.ToString()" не является литой какого-либо рода per se, это метод, реализованный объектом и, таким образом, так или иначе, каждым классом в .net, который "делает что-то" с экземпляр класса, который он вызывал, и возвращает строку.

Не забывайте, что для преобразования в строку также используется Convert.ToString(someType instanceOfThatType), где someType является одним из множества типов, по существу базовых типов фреймворков.

3

Кажется, что оба они концептуально отличаются.

Прямое литье

Типы не обязательно должны быть строго связаны. Он поставляется во всех видах ароматов.

  • Пользовательское неявное/явное кастинг: Обычно создается новый объект.
  • Тип значения Неявный: Копирование без потери информации.
  • Тип значения Явный: Копирование и информация могут быть потеряны.
  • Отношение IS-A: Измените ссылочный тип, в противном случае вы получите исключение.
  • Тот же тип: "Кастинг избыточен".

Похоже, что объект будет преобразован во что-то другое.

Оператор AS

Типы имеют прямую связь. Как в:

  • Типы ссылок: Связь IS-A. Объекты всегда одни и те же, только изменения ссылок.
  • Типы значений: Копировать бокс и типы с нулевым значением.

Похоже, вы собираетесь обрабатывать объект по-другому.

Образцы и 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
    }
3

Все полученные ответы хороши, если я могу что-то добавить: Чтобы напрямую использовать строковые методы и свойства (например, ToLower), вы не можете писать:

(string)o.ToLower(); // won't compile

вы можете писать только:

((string)o).ToLower();

но вместо этого вы можете написать:

(o as string).ToLower();

Опция as более читаема (по крайней мере, на мой взгляд).

  • 0
    Конструкция (o как строка) .ToLower () отрицает назначение оператора as. Это вызовет исключение нулевой ссылки, когда o не может быть приведен к строке.
  • 0
    @james - Но кто сказал, что единственная цель оператора as - генерировать исключение в случае сбоя приведения? Если вы знаете, что o является строкой и просто хотите написать более чистый код, вы можете использовать (o as string).ToLower() вместо нескольких запутанных скобок.
Показать ещё 3 комментария
3

В соответствии с экспериментами, запущенными на этой странице: http://www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as

(на этой странице иногда появляются ошибки "незаконного реферера", поэтому просто обновляйте, если это произойдет)

Вывод: оператор "как" обычно быстрее, чем приведение. Иногда во много раз быстрее, иногда просто быстрее.

Я воспринимаю нечто более "понятное".

Итак, поскольку он работает быстрее и "безопаснее" (wont throw exception), и, возможно, его легче читать, я рекомендую использовать "как" все время.

3
string s = o as string; // 2

Предпочитается, поскольку это позволяет избежать штрафа за производительность при двойном литье.

  • 0
    Привет Крис, ссылка, которая была в этом ответе, теперь 404 ... Я не уверен, что у тебя есть замена, которую ты хочешь поставить вместо нее?
0

В С# поддерживаются следующие две формы преобразования типов (литье):

|

(Резюме

• Преобразование статического типа v в c в данном выражении

• возможно только в том случае, если динамический тип v является c или подтипом c

• Если нет, исключается InvalidCastException

|

v как C

• Нефатальный вариант (c) v

• Таким образом, преобразуйте статический тип v в c в данном выражении

• Возвращает значение null, если динамический тип v не является c, или подтип c

0

Использовать прямой прилив 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;
 }
0

Так как никто не упомянул об этом, ближайший к instanceOf к Java по ключевым словам:

obj.GetType().IsInstanceOfType(otherObj)
0

При попытке получить строковое представление чего-либо (любого типа), потенциально имеющего нуль, я предпочитаю следующую строку кода. Он компактен, он вызывает ToString(), и он корректно обрабатывает нули. Если o равно null, s будет содержать String.Empty.

String s = String.Concat(o);

Ещё вопросы

Сообщество Overcoder
Наверх
Меню