简体   繁体   English

“as”关键字如何在内部工作?

[英]How does the “as” keyword work internally?

I know the function of this keyword, but I would like to know how it works on a lower level. 我知道这个关键字的功能,但我想知道它是如何在较低级别上工作的。

Which one is faster? 哪一个更快? And do they always yield the same result? 他们总能产生相同的结果吗? If they do, why are there two different ways? 如果他们这样做,为什么有两种不同的方式?

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

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

or 要么

Class123 obj = null;

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

According to MSDN: as (C# Reference) : 根据MSDN:as(C#参考)

The as operator is like a cast operation. as运算符就像一个强制转换操作。 However, if the conversion is not possible, as returns null instead of raising an exception. 但是,如果无法进行转换,则返回null而不是引发异常。 Consider the following expression: 请考虑以下表达式:

expression as type

It is equivalent to the following expression except that expression is evaluated only one time. 除了表达式仅被计算一次之外,它等效于以下表达式。

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

The first variant ( as operand ) ... 第一个变种( 作为操作数 )......

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

... compiles to this IL code: ...编译到这个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 

... and the second variant ( is operand + cast ) ... ......和第二个变体( 操作数+演员 )......

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

... compiles to this IL code: ...编译到这个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 

... so you see the only difference is the additional castclass code in line L_0038 . ...所以你看到的唯一区别是附加castclass在线路编码L_0038

There's no internal try-catch happening when using the as keyword. 使用as关键字时,没有内部尝试捕获。 The functionality is built in to the compiler/CLR, as far as I know, so the type check is implicit and automated. 据我所知,该功能内置于编译器/ CLR中,因此类型检查是隐式和自动的。

Simple rule : 简单的规则
Use a direct cast when you always expect the object to have a known type (and thus receive a helpful error if it is by chance of the wrong type). 使用直接施放时,你总是希望对象有一个已知类型 (因而收到一个有用的错误,如果它由错误类型的几率)。 Use the as keyword when the object is always of a known type . 当对象始终是已知类型时,请使用as关键字。

The reason for the existance of the as keyword is purely for the convenience of the programmer (although you are correct in suggesting that a try-catch would be slower). as关键字存在的原因纯粹是为了程序员的方便(尽管你建议try-catch会更慢)。 You could implement it yourself manually as such, as you point out: 你可以手动实现它,就像你指出的那样:

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

This highlights the fact that the 'as' keyword is primarily there for the purpose of conciseness. 这突出了'as'关键字主要用于简洁目的的事实。

Now, the performance difference between the two is likely to be negligible. 现在,两者之间的性能差异可能微不足道。 The as keyword is probably marginally slower because of the type check, but this is unlikely to affect code in the vast majority of situations. 由于类型检查, as关键字可能稍微慢一些,但在绝大多数情况下,这不太可能影响代码。 As oft said, premature optimisation is never a wise thing to be doing. 如上所述,过早优化从来都不是明智之举。 Benchmark if you really wish, but I would advise just to use whichever method is more convenient/appropiate for your situation, and not worry about performance at all (or later if you absolutely must). 基准如果你真的希望,但我建议只是使用哪种方法更方便/适合你的情况,而不是担心性能(或以后,如果你绝对必须)。

To set a few things straight: 直截了当地说几件事:

Type casting should be done when you are sure the object is of the type you're casting to. 当您确定对象属于您要转换的类型时,应该进行类型转换。 It can be null (in that case, null will be returned, unless it is a value type you're casting to) 它可以为null(在这种情况下,将返回null,除非它是您要转换的值类型)

When you are not sure, the "as" operator can be used. 如果您不确定,可以使用“as”运算符。 When the object is not castable, or the object is null, null will be returned. 当对象不可转换,或者对象为null时,将返回null

The "as" operator translates to a dedicated IL statement ( isinst ), while a type cast translates to the castclass IL statement, so it is built into the runtime. “as”运算符转换为专用的IL语句( isinst ),而类型转换转换为castclass IL语句,因此它构建在运行时中。 The compiler simply emits the correct IL statement. 编译器只发出正确的IL语句。

因为可能更快,因为只需要将类型ckeck一次,而+ cast需要检查类型两次。

This question has already been answered well, however so far it has been missing hard numbers. 这个问题已经得到了很好的回答,但到目前为止它已经缺少了难以理解的数字。

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

The figures consistently come back in these proportions 这些数字一直以这些比例回归

I want to point out that the only conclusion to take from these figures is that from a performance point of view, there is very little to be gained by choosing one of these methods over the other . 我想指出, 从这些数字中得出的唯一结论是,从绩效的角度来看,通过选择其中一种方法而获得的结果很少 There's a very little in the difference for a single call (where very little tends to zero). 单个呼叫的差异很小(非常小的趋势为零)。 That said, "as" is faster :) 那说,“as”更快:)

After that, the above figures mostly stand to reason. 在此之后,上述数字大多是有道理的。

"As" takes longer on failure than it does on success. “作为”失败的时间比成功时长。 On success nothing happens, the value can be used as is, or simply copied. 成功没有任何反应,可以按原样使用,也可以简单地复制。 On failure it requires a jump to copy a null reference. 失败时,需要跳转才能复制空引用。

"Cast" is faster on failure, one call to "is" and it doesn't do any more. 失败时“强制转换”速度更快,一次调用“是”并且它不再执行任务。 On success it's much slower, it has the over head of the call to "is" and then the cast. 成功时它会慢得多,它会对“是”和“演员”进行调整。

However I'm surprised that Cast on failure takes longer than AS failure 但是我很惊讶Cast失败的时间比AS失败的时间长

Edit 编辑

As requested, figures for cast in a try / catch block 根据要求,在try / catch块中投射的数字

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

The code that produced the first set of figures 产生第一组数字的代码

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM