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

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

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

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

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

Ответ 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 может делать то, что вы хотите.

Ответ 4

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

Ответ 5

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

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

Ответ 7

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

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

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

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

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

Ответ 8

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

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

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

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

Ответ 9

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

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

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

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

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

Ответ 10

string s = o as string; // 2

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

Ответ 11

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

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

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

((string)o).ToLower();

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

(o as string).ToLower();

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

Ответ 12

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

Прямое литье

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

  • Пользовательское неявное/явное кастинг: Обычно создается новый объект.
  • Тип значения Неявный: Копирование без потери информации.
  • Тип значения Явный: Копирование и информация могут быть потеряны.
  • Отношение 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
    }

Ответ 13

Я хотел бы обратить внимание на следующие особенности оператора as:

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/as

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

Ответ 14

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

String s = String.Concat(o);

Ответ 15

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

obj.GetType().IsInstanceOfType(otherObj)

Ответ 16

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

Ответ 17

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

|

(Резюме

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

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

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

|

v как C

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

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

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