Приведение против использования ключевого слова as в CLR - PullRequest
367 голосов
/ 30 января 2009

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

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

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);
    }
}

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

Ответы [ 18 ]

1 голос
/ 08 декабря 2015

Если вы используете 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 голос
/ 20 июля 2015

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

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 голос
/ 25 сентября 2013

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

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 голосов
/ 02 июля 2017

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

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 голосов
/ 08 февраля 2013

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

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

0 голосов
/ 30 января 2009

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

IMyInterface = (IMyInterface)someobj;

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

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

0 голосов
/ 30 января 2009

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

0 голосов
/ 30 января 2009

Зависит от того, хотите ли вы проверять наличие нуля после использования «как» или вы бы предпочли, чтобы ваше приложение выдало исключение?

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

...