Прямое приведение против оператора "как"? - PullRequest
651 голосов
/ 25 сентября 2008

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

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
}

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

Ответы [ 17 ]

778 голосов
/ 25 сентября 2008
string s = (string)o; // 1

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

324 голосов
/ 25 сентября 2008
  1. string s = (string)o; Используйте, когда что-то должно определенно будет другим.
  2. string s = o as string; Используется, когда что-то может быть другим вещь.
  3. string s = o.ToString(); Используйте, когда вам все равно, что но вы просто хотите использовать Доступное строковое представление.
26 голосов
/ 25 сентября 2008

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

9 голосов
/ 25 сентября 2008

Если вы уже знаете, к какому типу он может быть приведен, используйте приведение в стиле 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;

Обратите внимание, что при не будет вызывать операторы преобразования типов. Он будет ненулевым, только если объект не нулевой и изначально имеет указанный тип.

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

7 голосов
/ 25 сентября 2008

Ключевое слово 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 голосов
/ 25 сентября 2008

'as' основан на 'is', который является ключевым словом, которое проверяет во время выполнения, является ли объект полиморфно-совместимым (в основном, если приведение может быть выполнено) и возвращает ноль, если проверка не удалась.

Эти два эквивалента:

Использование «как»:

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;

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

5 голосов
/ 25 сентября 2008

2 полезно для приведения к производному типу.

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

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

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

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

4 голосов
/ 15 августа 2010

Согласно экспериментам на этой странице: http://www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as

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

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

Я лично считаю, что «как» также более читабельно.

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

4 голосов
/ 25 сентября 2008

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

«o as string» приведет к тому, что s будет пустой ссылкой, а не будет выброшено исключение.

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

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

3 голосов
/ 25 сентября 2016

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

Прямое литье

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

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

Такое ощущение, что объект будет преобразован во что-то еще.

как оператор

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

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

Такое ощущение, что вы собираетесь обращаться с объектом по-другому.

Образцы и 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
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...