Приведение против использования ключевого слова as в CLR

337

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

Есть ли разница между этими двумя методами преобразования? Если да, есть ли разница в стоимости или как это влияет на мою программу?

public interface IMyInterface
{
    void AMethod();
}

public class MyClass : IMyInterface
{
    public void AMethod()
    {
       //Do work
    }

    // Other helper methods....
}

public class Implementation
{
    IMyInterface _MyObj;
    MyClass _myCls1;
    MyClass _myCls2;

    public Implementation()
    {
        _MyObj = new MyClass();

        // What is the difference here:
        _myCls1 = (MyClass)_MyObj;
        _myCls2 = (_MyObj as MyClass);
    }
}

Кроме того, что такое "вообще" предпочтительный метод?

  • 0
    Не могли бы вы добавить небольшой пример того, почему вы в первую очередь используете приведение к вопросу, или, возможно, начать новый? Меня интересует, почему вам нужен актерский состав только для юнит-тестирования. Я думаю, что это выходит за рамки этого вопроса, хотя.
  • 2
    Я, вероятно, могу изменить свой юнит-тест, чтобы избежать этой необходимости. По сути, это сводится к тому, что у меня есть свойство конкретного объекта, которого нет в интерфейсе. Мне нужно установить это свойство, но в реальной жизни это свойство было бы установлено другими способами. Это отвечает на ваш вопрос?
Показать ещё 1 комментарий
Теги:
casting
clr

18 ответов

495
Лучший ответ

Ответ ниже строки был написан в 2008 году.

С# 7 ввел сопоставление образцов, которое в значительной степени заменило оператор as, как вы теперь можете написать:

if (randomObject is TargetType tt)
{
    // Use tt here
}

Обратите внимание, что tt по-прежнему находится в области видимости после этого, но определенно не определен. (Это определенно назначается внутри тела if). Это немного раздражает в некоторых случаях, поэтому, если вам действительно интересно ввести наименьшее количество переменных, возможных в каждой области, вы все равно можете использовать is, за которым следует бросок.


Я не думаю, что какой-либо из ответов до сих пор (во время запуска этого ответа!) действительно объяснил, где это стоит использовать.

  • Не делайте этого:

    // Bad code - checks type twice for no reason
    if (randomObject is TargetType)
    {
        TargetType foo = (TargetType) randomObject;
        // Do something with foo
    }
    

    Эта проверка не только дважды проверяется, но может проверять разные вещи, если randomObject - поле, а не локальная переменная. Возможно, что "if" пройдет, а затем приведение к отказу, если другой поток изменит значение randomObject между ними.

  • Если randomObject действительно должен быть экземпляром TargetType, т.е. если это не так, значит, есть ошибка, тогда отлитие - правильное решение. Это немедленно вызывает исключение, а это означает, что при неправильных предположениях больше не выполняется работа, а исключение корректно показывает тип ошибки.

    // This will throw an exception if randomObject is non-null and
    // refers to an object of an incompatible type. The cast is
    // the best code if that the behaviour you want.
    TargetType convertedRandomObject = (TargetType) randomObject;
    
  • Если randomObject может быть экземпляром TargetType, а TargetType является ссылочным типом, используйте следующий код:

    TargetType convertedRandomObject = randomObject as TargetType;
    if (convertedRandomObject != null)
    {
        // Do stuff with convertedRandomObject
    }
    
  • Если randomObject может быть экземпляром TargetType, а TargetType - тип значения, то мы не можем использовать as только с TargetType, но мы можем использовать тип с нулевым значением:

    TargetType? convertedRandomObject = randomObject as TargetType?;
    if (convertedRandomObject != null)
    {
        // Do stuff with convertedRandomObject.Value
    }
    

    (Примечание: в настоящее время это на самом деле медленнее, чем + cast. Я думаю, что он более изящный и последовательный, но мы идем.)

  • Если вам действительно не нужно преобразованное значение, но вам просто нужно знать, является ли это экземпляром TargetType, тогда оператор is является вашим другом. В этом случае не имеет значения, является ли TargetType ссылочным типом или типом значения.

  • Могут быть другие случаи, связанные с дженериками, где is полезен (потому что вы не можете знать, является ли T ссылочным типом или нет, поэтому вы не можете использовать его как), но они относительно неясны.

  • Я почти наверняка использовал is для случая типа значения до этого момента, не подумав об использовании типа с возможностью NULL и as вместе:)


EDIT: Обратите внимание, что ни одно из вышеперечисленных вопросов о производительности, отличное от случая типа значения, где я заметил, что unboxing для типа значения NULL на самом деле медленнее, но непротиворечиво.

В соответствии с ответом на наследование, "is-and-cast" или "is-and-as", как и как-и-null-check с современными JIT, как показано ниже:

using System;
using System.Diagnostics;
using System.Linq;

class Test
{
    const int Size = 30000000;

    static void Main()
    {
        object[] values = new object[Size];
        for (int i = 0; i < Size - 2; i += 3)
        {
            values[i] = null;
            values[i + 1] = "x";
            values[i + 2] = new object();
        }
        FindLengthWithIsAndCast(values);
        FindLengthWithIsAndAs(values);
        FindLengthWithAsAndNullCheck(values);
    }

    static void FindLengthWithIsAndCast(object[] values)        
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            if (o is string)
            {
                string a = (string) o;
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("Is and Cast: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithIsAndAs(object[] values)        
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            if (o is string)
            {
                string a = o as string;
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("Is and As: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithAsAndNullCheck(object[] values)        
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            string a = o as string;
            if (a != null)
            {
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("As and null check: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }
}

На моем ноутбуке все они выполняются примерно в 60 мс. Следует отметить две вещи:

  • Между ними нет существенной разницы. (На самом деле, есть ситуации, в которых проверка "плюс-нуль" определенно медленнее. Вышеприведенный код фактически упрощает проверку типа, потому что он для закрытого класса, если вы проверяете интерфейс, в пользу as-plus-null-check.)
  • Они все безумно быстро. Это просто не будет узким местом в вашем коде, если вы действительно ничего не собираетесь делать со значениями после этого.

Так что не беспокойтесь о производительности. Позвольте беспокоиться о правильности и последовательности.

Я утверждаю, что is-and-cast (или is-and-as) являются небезопасными при работе с переменными, поскольку тип значения, на который он ссылается, может измениться из-за другого потока между тестом и литой. Это была бы довольно редкая ситуация, но я предпочел бы соглашение, которое я могу использовать последовательно.

Я также утверждаю, что проверка "как-то-нуль" дает лучшее разделение проблем. У нас есть одно выражение, которое пытается преобразовать, а затем один оператор, который использует результат. Функция is-and-cast или is-and-as выполняет тест, а затем другую попытку преобразования значения.

Иными словами, кто-нибудь когда-нибудь напишет:

int value;
if (int.TryParse(text, out value))
{
    value = int.Parse(text);
    // Use value
}

Это то, что делают-и-литые, хотя, очевидно, довольно дешево.

  • 0
    так что вы упоминаете, что оператор "is" в этой ситуации неправильный, но когда можно использовать "is". Спасибо
  • 0
    Редактирование ответа, чтобы объяснить это.
Показать ещё 18 комментариев
66

", поскольку" возвращает NULL, если это невозможно сделать.

кастинг перед вызовет исключение.

Для производительности повышение исключения обычно более дорогое время.

  • 3
    Повышение исключений обходится дороже, но если вы знаете, что объект может быть наложен правильно, так как требуется больше времени из-за проверки безопасности (см. Ответ Антона). Однако стоимость проверки безопасности, я считаю, довольно мала.
  • 16
    Стоимость потенциального создания исключения является фактором, который необходимо учитывать, но часто это правильный дизайн.
Показать ещё 3 комментария
25

Вот еще один ответ, с некоторым сравнением IL. Рассмотрим класс:

public class MyClass
{
    public static void Main()
    {
        // Call the 2 methods
    }

    public void DirectCast(Object obj)
    {
        if ( obj is MyClass)
        { 
            MyClass myclass = (MyClass) obj; 
            Console.WriteLine(obj);
        } 
    } 


    public void UsesAs(object obj) 
    { 
        MyClass myclass = obj as MyClass; 
        if (myclass != null) 
        { 
            Console.WriteLine(obj);
        } 
    }
}

Теперь посмотрим на IL, который производит каждый метод. Даже если коды op ничего не означают для вас, вы можете увидеть одно существенное различие - isinst вызывается, за которым следует castclass в методе DirectCast. Таким образом, два вызова вместо одного в основном.

.method public hidebysig instance void  DirectCast(object obj) cil managed
{
  // Code size       22 (0x16)
  .maxstack  8
  IL_0000:  ldarg.1
  IL_0001:  isinst     MyClass
  IL_0006:  brfalse.s  IL_0015
  IL_0008:  ldarg.1
  IL_0009:  castclass  MyClass
  IL_000e:  pop
  IL_000f:  ldarg.1
  IL_0010:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_0015:  ret
} // end of method MyClass::DirectCast

.method public hidebysig instance void  UsesAs(object obj) cil managed
{
  // Code size       17 (0x11)
  .maxstack  1
  .locals init (class MyClass V_0)
  IL_0000:  ldarg.1
  IL_0001:  isinst     MyClass
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  brfalse.s  IL_0010
  IL_000a:  ldarg.1
  IL_000b:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_0010:  ret
} // end of method MyClass::UsesAs

Ключевое слово isinst против класса cast

Это сообщение в блоге имеет достойное сравнение между двумя способами его выполнения. Его резюме:

  • В прямом сравнении isinst быстрее, чем castclass (хотя и немного)
  • При выполнении проверок, обеспечивающих успешное преобразование, isinst был значительно быстрее, чем castclass
  • Комбинация isinst и castclass не должна использоваться, поскольку это было намного медленнее, чем быстрое "безопасное" преобразование (более 12% медленнее).

Я лично всегда использую As, потому что он легко читается и рекомендуется разработчиком .NET(или Джеффри Рихтером) в любом случае

  • 0
    Я искал четкое объяснение для кастинга против, так как этот ответ делает его более понятным, поскольку включает в себя общий промежуточный язык пошаговое объяснение. Спасибо!
18

Одно из самых тонких различий между ними заключается в том, что ключевое слово "as" не может использоваться для кастинга, когда задействован оператор трансляции:

public class Foo
{
    public string Value;

    public static explicit operator string(Foo f)
    {
        return f.Value;
    }

}

public class Example
{
    public void Convert()
    {
        var f = new Foo();
        f.Value = "abc";

        string cast = (string)f;
        string tryCast = f as string;
    }
}

Это не будет компилироваться (хотя я думаю, что это было в предыдущих версиях) в последней строке, так как ключевые слова "as" не учитывают приведение операторов-операторов. Строка string cast = (string)f; работает отлично, хотя.

12

поскольку никогда не генерирует исключение, если оно не может выполнить преобразование, возвращающее значение null (как работает только для ссылочных типов). Поэтому использование as в основном эквивалентно

_myCls2 = _myObj is MyClass ? (MyClass)_myObj : null;
С другой стороны,

C-style casts генерирует исключение, если невозможно преобразование.

  • 4
    Эквивалентно, да, но не то же самое. Это генерирует гораздо больше кода, чем как.
10

Не совсем ответ на ваш вопрос, но то, что я думаю, является важной связанной точкой.

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

  • 0
    Пока что кастинг в основном был необходим для моего юнит-тестирования, но спасибо, что подняли его. Я буду помнить об этом, пока буду работать над этим.
  • 0
    Согласившись с жабой, мне также любопытно, почему аспект модульного тестирования имеет отношение к кастингу для вас @Frank V. Там, где есть необходимость в кастинге, часто возникает необходимость в редизайне или рефакторинге, так как он предполагает, что вы пытаетесь рожать различные проблемы, где они должны решаться по-разному.
Показать ещё 1 комментарий
9

Пожалуйста, игнорируйте совет Джона Скита, повторите: избегайте тестового шаблона, т.е.:

if (randomObject is TargetType)
{
    TargetType foo = randomObject as TargetType;
    // Do something with foo
}

Идея, что это стоит больше, чем листинг и нулевой тест, - это MYTH:

TargetType convertedRandomObject = randomObject as TargetType;
if (convertedRandomObject != null)
{
    // Do stuff with convertedRandomObject
}

Это микро-оптимизация, которая не работает. Я выполнил некоторые реальные тесты, а test-and-cast на самом деле быстрее, чем сравнение cast-and-null, и это более безопасно, потому что вы надеваете 't иметь возможность иметь нулевую ссылку в области вне if, если при отказе бросается.

Если вам нужна причина, почему тест-литье выполняется быстрее или, по крайней мере, не медленнее, существует простая и сложная причина.

Простой: даже наивные компиляторы объединит две подобные операции, такие как test-and-cast, в один тест и ветвь. cast-and-null-test может заставлять два теста и ветку, одну для теста типа и преобразование в null при ошибке, одну для самой нулевой проверки. По крайней мере, они оба будут оптимизированы для одного теста и ветки, поэтому тест-литье не будет ни медленнее, ни быстрее, чем тест cast-and-null.

Сложность:, почему тестирование и литье происходит быстрее: cast-and-null-test вводит другую переменную во внешнюю область, которую компилятор должен отслеживать для жизнеспособности, и может быть не в состоянии оптимизировать в зависимости от того, насколько сложным является ваш поток управления. И наоборот, test-and-cast вводит новую переменную только в ограниченной области, поэтому компилятор знает, что переменная мертва после выхода области, и поэтому может оптимизировать распределение регистров лучше.

Итак, пожалуйста, ПОЖАЛУЙСТА, пусть это "литье и нуль-тест лучше, чем тест-литье". ПОЖАЛУЙСТА. test-and-cast безопаснее и быстрее.

  • 0
    Я ценю ваш спорный, подробный ответ. Я не хочу переключать «ответ» на этот вопрос просто на основании вашего объяснения (хотя я понимаю вашу точку зрения). Фактически, ответ Скита является популярным ответом (наряду с приданием мне технического смысла). Я, конечно, не говорю, что вы не в чём-то, но я хотел бы получить дополнительную поддержку. Возможно, другие завершили подобное исследование. Если вы можете найти его, опубликуйте его.
  • 0
    Ну, исходный код доступен из моих тестов, так что вы можете запустить их самостоятельно, если хотите. Мне было бы интересно услышать ваши результаты.
Показать ещё 5 комментариев
4

Это не ответ на вопрос, а комментарий к примеру кода вопроса:

Обычно вам не нужно бросать объект из, например. IMyInterface для MyClass. Самое замечательное в интерфейсах заключается в том, что если вы берете объект как входной, который реализует интерфейс, вам не нужно заботиться о том, какой объект вы получаете.

Если вы передаете IMyInterface в MyClass, вы уже предполагаете, что получаете объект типа MyClass, и нет смысла использовать IMyInterface, потому что если вы будете кормить код другими классами, которые реализуют IMyInterface, это сломает ваш код...

Теперь, мой совет: если ваши интерфейсы хорошо разработаны, вы можете избежать многого типа.

4

Если приведение не выполняется, ключевое слово 'as' не генерирует исключение; он устанавливает переменную в значение null (или ее значение по умолчанию для типов значений).

  • 3
    Нет значений по умолчанию для типов значений. Как нельзя использовать для приведения типов значения.
  • 2
    Ключевое слово «as» фактически не работает с типами значений, поэтому оно всегда имеет значение null.
3

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

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

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

  • 1
    'as' также может использоваться в типах значений, допускающих значение NULL, что обеспечивает интересный шаблон. Смотрите мой ответ для кода.
1

Если вы используете Office PIA с таргетингом на .NET Framework 4.X, вы должны использовать ключевое слово как, иначе оно не будет компилироваться.

Microsoft.Office.Interop.Outlook.Application o = new Microsoft.Office.Interop.Outlook.Application();
Microsoft.Office.Interop.Outlook.MailItem m = o.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem) as Microsoft.Office.Interop.Outlook.MailItem;

Кастинг подходит для таргетинга .NET 2.0:

Microsoft.Office.Interop.Outlook.MailItem m = (Microsoft.Office.Interop.Outlook.MailItem)o.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem);

При настройке .NET 4.X ошибки:

ошибка CS0656: отсутствующий компилятор требует член 'Microsoft.CSharp.RuntimeBinder.Binder.Convert'

ошибка CS0656: отсутствующий компилятор, требуемый член 'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create'

1

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

var x = (T) ...

по сравнению с использованием оператора as.

Вот пример:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(GenericCaster<string>(12345));
        Console.WriteLine(GenericCaster<object>(new { a = 100, b = "string" }) ?? "null");
        Console.WriteLine(GenericCaster<double>(20.4));

        //prints:
        //12345
        //null
        //20.4

        Console.WriteLine(GenericCaster2<string>(12345));
        Console.WriteLine(GenericCaster2<object>(new { a = 100, b = "string" }) ?? "null");

        //will not compile -> 20.4 does not comply due to the type constraint "T : class"
        //Console.WriteLine(GenericCaster2<double>(20.4));
    }

    static T GenericCaster<T>(object value, T defaultValue = default(T))
    {
        T castedValue;
        try
        {
            castedValue = (T) Convert.ChangeType(value, typeof(T));
        }
        catch (Exception)
        {
            castedValue = defaultValue;
        }

        return castedValue;
    }

    static T GenericCaster2<T>(object value, T defaultValue = default(T)) where T : class
    {
        T castedValue;
        try
        {
            castedValue = Convert.ChangeType(value, typeof(T)) as T;
        }
        catch (Exception)
        {
            castedValue = defaultValue;
        }

        return castedValue;
    }
}

Нижняя строка: GenericCaster2 не будет работать со структурами. GenericCaster будет.

1

Мой ответ касается только скорости в тех случаях, когда мы не проверяем тип и не проверяем нули после кастинга. Я добавил два дополнительных теста коду Jon Skeet:

using System;
using System.Diagnostics;

class Test
{
    const int Size = 30000000;

    static void Main()
    {
        object[] values = new object[Size];

        for (int i = 0; i < Size; i++)
        {
            values[i] = "x";
        }
        FindLengthWithIsAndCast(values);
        FindLengthWithIsAndAs(values);
        FindLengthWithAsAndNullCheck(values);

        FindLengthWithCast(values);
        FindLengthWithAs(values);

        Console.ReadLine();
    }

    static void FindLengthWithIsAndCast(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            if (o is string)
            {
                string a = (string)o;
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("Is and Cast: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithIsAndAs(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            if (o is string)
            {
                string a = o as string;
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("Is and As: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithAsAndNullCheck(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            string a = o as string;
            if (a != null)
            {
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("As and null check: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }
    static void FindLengthWithCast(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            string a = (string)o;
            len += a.Length;
        }
        sw.Stop();
        Console.WriteLine("Cast: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithAs(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            string a = o as string;
            len += a.Length;
        }
        sw.Stop();
        Console.WriteLine("As: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }
}

Результат:

Is and Cast: 30000000 : 88
Is and As: 30000000 : 93
As and null check: 30000000 : 56
Cast: 30000000 : 66
As: 30000000 : 46

Не пытайтесь сосредоточиться на скорости (как и я), потому что все это очень быстро.

  • 0
    Кроме того , в ходе тестирования я обнаружил , что , as преобразование (без проверки ошибок) бегала 1-3% быстрее , чем литье (около 540ms против 550ms на 100 миллионов итераций). Ни один не сделает или сломает ваше заявление.
0

Проблема ОП ограничивается конкретной ситуацией каста. Название охватывает гораздо больше ситуаций. Вот обзор всех соответствующих ситуаций каста, о которых я в настоящее время могу думать:

private class CBase
{
}

private class CInherited : CBase
{
}

private enum EnumTest
{
  zero,
  one,
  two
}

private static void Main (string[] args)
{
  //########## classes ##########
  // object creation, implicit cast to object
  object oBase = new CBase ();
  object oInherited = new CInherited ();

  CBase oBase2 = null;
  CInherited oInherited2 = null;
  bool bCanCast = false;

  // explicit cast using "()"
  oBase2 = (CBase)oBase;    // works
  oBase2 = (CBase)oInherited;    // works
  //oInherited2 = (CInherited)oBase;   System.InvalidCastException
  oInherited2 = (CInherited)oInherited;    // works

  // explicit cast using "as"
  oBase2 = oBase as CBase;
  oBase2 = oInherited as CBase;
  oInherited2 = oBase as CInherited;  // returns null, equals C++/CLI "dynamic_cast"
  oInherited2 = oInherited as CInherited;

  // testing with Type.IsAssignableFrom(), results (of course) equal the results of the cast operations
  bCanCast = typeof (CBase).IsAssignableFrom (oBase.GetType ());    // true
  bCanCast = typeof (CBase).IsAssignableFrom (oInherited.GetType ());    // true
  bCanCast = typeof (CInherited).IsAssignableFrom (oBase.GetType ());    // false
  bCanCast = typeof (CInherited).IsAssignableFrom (oInherited.GetType ());    // true

  //########## value types ##########
  int iValue = 2;
  double dValue = 1.1;
  EnumTest enValue = EnumTest.two;

  // implicit cast, explicit cast using "()"
  int iValue2 = iValue;   // no cast
  double dValue2 = iValue;  // implicit conversion
  EnumTest enValue2 = (EnumTest)iValue;  // conversion by explicit cast. underlying type of EnumTest is int, but explicit cast needed (error CS0266: Cannot implicitly convert type 'int' to 'test01.Program.EnumTest')

  iValue2 = (int)dValue;   // conversion by explicit cast. implicit cast not possible (error CS0266: Cannot implicitly convert type 'double' to 'int')
  dValue2 = dValue;
  enValue2 = (EnumTest)dValue;  // underlying type is int, so "1.1" beomces "1" and then "one"

  iValue2 = (int)enValue;
  dValue2 = (double)enValue;
  enValue2 = enValue;   // no cast

  // explicit cast using "as"
  // iValue2 = iValue as int;   error CS0077: The as operator must be used with a reference type or nullable type
}
0

Посмотрите на эти ссылки:

они показывают вам некоторые детали и тесты производительности.

0

То, что вы выбираете, сильно зависит от того, что требуется. Я предпочитаю явное литье

IMyInterface = (IMyInterface)someobj;

потому что если объект должен иметь тип IMyInterface, а это не так - это определенно проблема. Лучше получить ошибку как можно раньше, потому что точная ошибка будет исправлена ​​вместо фиксации ее побочного эффекта.

Но если вы имеете дело с методами, которые принимают object как параметр, тогда вам нужно проверить его точный тип перед выполнением какого-либо кода. В таком случае as было бы полезно, поэтому вы можете избежать InvalidCastException.

0

Это зависит от того, хотите ли вы проверить значение null после использования "как" или хотите ли вы, чтобы ваше приложение выбрало исключение?

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

0

Ключевое слово as работает так же, как явное приведение между совместимыми типами ссылок с большой разницей, что оно не вызывает исключения, если преобразование не выполняется. Скорее, он дает нулевое значение в целевой переменной. Поскольку Исключения очень дороги с точки зрения производительности, это считается гораздо лучшим методом кастинга.

  • 0
    Не то же самое, что один вызывает CastClass, а другой вызывает IsInst в коде IL.
Сообщество Overcoder
Наверх
Меню