Как ключевое слово «как» работает внутри? - PullRequest
8 голосов
/ 05 июня 2009

Я знаю функцию этого ключевого слова, но мне хотелось бы знать, как оно работает на более низком уровне.

Какой из них быстрее? И всегда ли они дают один и тот же результат? Если они это делают, то почему есть два разных способа?

// Is there an overhead? An internal try catch?
Class123 obj = someobject as Class123;

if (Class123 != null)
{
    //OK
}

или

Class123 obj = null;

if (someobject is Class123)
{
    obj = (Class123)someobject;
}

Ответы [ 5 ]

22 голосов
/ 05 июня 2009

Согласно MSDN: as (C # Reference) :

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

expression as type

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

expression is type ? (type)expression : (type)null

Первый вариант ( в качестве операнда ) ...

string str1 = strAsObject as string;
if (str1 != null)
{
    this.blabla(str1);
}

... компилируется в этот код IL:

L_0009: ldloc.1 
L_000a: isinst string
L_000f: stloc.2 
L_0010: ldloc.2 
L_0011: ldnull 
L_0012: ceq 
L_0014: stloc.s CS$4$0000
L_0016: ldloc.s CS$4$0000
L_0018: brtrue.s L_0024
L_001a: nop 
L_001b: ldarg.0 
L_001c: ldloc.2 
L_001d: call instance void TestWinFormsApplication001.Form1::blabla(string)
L_0022: nop 
L_0023: nop 

... и второй вариант ( операнд + приведение ) ...

if (strAsObject is string)
{
    string str2 = (string) strAsObject;
    this.blabla(str2);
}

... компилируется в этот код IL:

L_0024: ldloc.1 
L_0025: isinst string
L_002a: ldnull 
L_002b: cgt.un 
L_002d: ldc.i4.0 
L_002e: ceq 
L_0030: stloc.s CS$4$0000
L_0032: ldloc.s CS$4$0000
L_0034: brtrue.s L_0047
L_0036: nop 
L_0037: ldloc.1 
L_0038: castclass string
L_003d: stloc.3 
L_003e: ldarg.0 
L_003f: ldloc.3 
L_0040: call instance void TestWinFormsApplication001.Form1::blabla(string)
L_0045: nop 
L_0046: nop 

... так что вы видите единственное отличие - дополнительный код castclass в строке L_0038.

16 голосов
/ 05 июня 2009

При использовании ключевого слова as внутренняя попытка перехвата не происходит. Насколько я знаю, функциональность встроена в компилятор / CLR, поэтому проверка типов неявна и автоматизирована.

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

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

var castObj = (obj is NewType) ? (NewType)obj : null;

Это подчеркивает тот факт, что ключевое слово «как» используется в основном для краткости.

Теперь разница в производительности между ними, вероятно, будет незначительной. Ключевое слово as, вероятно, немного медленнее из-за проверки типа, но это вряд ли повлияет на код в подавляющем большинстве ситуаций. Как уже говорилось, преждевременная оптимизация никогда не бывает мудрой. Оцените, если вы действительно хотите, но я бы посоветовал просто использовать тот метод, который более удобен / подходит для вашей ситуации, и вообще не беспокоиться о производительности (или позже, если вам абсолютно необходимо).

9 голосов
/ 05 июня 2009

Чтобы объяснить несколько вещей:

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

Если вы не уверены, можно использовать оператор «как». Если объект не подлежит преобразованию или объект имеет значение null, будет возвращено null .

Оператор «as» преобразуется в выделенный оператор IL ( isinst ), тогда как приведение типа преобразуется в оператор castclass IL, поэтому он встроен в среду выполнения. Компилятор просто выдает правильный оператор IL.

5 голосов
/ 05 июня 2009

На этот вопрос уже был получен хороший ответ, однако до сих пор не было точных цифр.

Over 100000000 iterations
AS   : Failure  00:00:00.9282403
Cast : Failure  00:00:00.9868966
AS   : Success  00:00:00.9350227
Cast : Success  00:00:01.1382759

Цифры последовательно возвращаются в этих пропорциях

Я хочу отметить, что единственный вывод, который можно сделать из этих цифр, состоит в том, что с точки зрения производительности, очень мало чего можно добиться, выбрав один из этих методов над другим . Разница в одном вызове очень мала (где очень мало стремится к нулю). Тем не менее, "как" быстрее:)

После этого приведенные выше цифры в основном соответствуют разуму.

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

«Приведение» происходит быстрее при сбое, один вызов «есть» и больше не работает. В случае успеха это намного медленнее, у него есть перевес вызова «есть», а затем приведение.

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

Редактировать

По запросу цифры для броска в блоке try / catch

Over 100000000 iterations
Catch : Failure 05.05:00:00 // approximately, because I didn't hang around
Catch : Success 00:00:01.4000952

Код, который произвел первый набор цифр

class Program
{
    const int ITERATION_COUNT = 100000000;
    private static UInt64 stringCount = 0;
    private static UInt64 objectCount = 0;
    static void Main(string[] args)
    {
        Console.WriteLine("Over {0} iterations ", ITERATION_COUNT);

        string s = "Hello";
        object o = new Int32();

        RunTest("AS   : Failure  {0}", TestAs, o);
        RunTest("Cast : Failure  {0}", TestIs_And_Cast, o);
        RunTest("AS   : Success  {0}", TestAs, s);
        RunTest("Cast : Success  {0}", TestIs_And_Cast, s);

        Console.WriteLine("Press any key to stop");
        Console.ReadKey();

    }
    private static void RunTest(string testDescription, Action<object> testToRun, object arg)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        for (int i = 0; i < ITERATION_COUNT; i++)
            testToRun(arg);
        sw.Stop();
        Console.WriteLine(testDescription, sw.Elapsed);
    }
    static void TestAs(object obj)
    {
        string s = obj as string;
        if (s != null)
            stringCount++;
        else
            objectCount++;
    }
    static void TestIs_And_Cast(object obj)
    {
        string s = null;
        if (obj is string)
        {
            s = (string)obj;
            stringCount++;
        }
        else
            objectCount++;
    }
}
5 голосов
/ 05 июня 2009

как может быть быстрее, потому что нужно проверять тип только один раз, в то время как + cast необходимо проверять тип дважды.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...