Why is 'is' implemented as 'as'?

烈酒焚心 提交于 2019-12-03 01:12:06

a) Is this correct

Yes, though I would have stated it the other way. You are saying that "is" is a syntactic sugar for as-followed-by-null-check. I would have said it the other way: that "as" is a syntactic sugar for "check for type implementation, cast if success, null if failure".

That is to say, I would be more inclined to say

if (x is Bar) { 
   Bar y = x as Bar; 
   something(); 
} 

is effectively equivalent to

if (x is Bar) { 
   Bar y = (x is Bar) ? (Bar)x : (Bar) null; 
   something(); 
} 

See, you want to define "as" in terms of "is", not the other way around. The question really should be "why is as implemented as is?" :-)

b) If so, why is is implemented like that?

Because that's a correct implementation of the specification.

I think I'm not following your line of thought here. Is there something wrong with that implementation? How would you prefer it to be implemented? You have the "isinst" and "castclass" instructions at your disposal; describe the codegen for your program that you'd like to see.

Well, the IL instruction that is available (isinst) will return either an object of the appropriate type, or null if such a conversion is not possible. And it doesn't throw an exception if the conversion isn't possible.

Given that, both "is" and "as" are trivial to implement. I wouldn't claim that "is" is implemented as "as" in this case, just that the underlying IL instruction allows both to occur. Now, why the compiler isn't able to optimize the "is" followed by "as" into a single isinst call, that's another matter. Probably, in this case, it's related to variable scope (even though by the time this is IL, scope doesn't really exist)

Edit

On second thoughts, you can't optimise "is" followed by "as" into a single isinst call, without knowing that the variable under discussion isn't subject to update from other threads.

Assuming x is a string:

//Thread1
if(x is string)

//Thread2
x = new ComplexObject();

//Thread1
    y = x as string

Here, y should be null.

In your example, the use of as is redundant anyway. Since you already know that x is Bar, you should be using a cast:

if (x is Bar)
{
    Bay y = (Bar)x;
}

Alternatively, convert using as and just check for null:

Bar y = x as Bar;
if (y != null)
{

}

Firstly I disagree with your premise that this is more typical use case. It may be your favourite approach, but the idiomatic approach is the "as + null check" style:

Bar y = x as Bar; 
if (y != null) { 
   something(); 
}

As you have found the "is" approach requires the extra "as" or a cast, which is why the "as" with null check is the standard way of doing this in my experience.

I see nothing offensive about this "as" approach, personally I don't think it any more unpleasant on the eye than any other code.

As to your actual question, why is the is keyword implemented in terms of the as keyword, I have no idea, but I do like the play on words in your question:) I suspect neither is actually implemented in terms of the other, but the tool (Reflector I guess) you used to generate C# from the IL interpreted the IL in terms of as.

You won't do a second y = x as Bar;, because your already have y which is Bar.

You could write the code now as

DoIfOfType<Bar>(possibleBar, b => b.something())

That I would say was a bit clearer, but not as fast without real magic from the compiler.

shf301

According to the blog post How Many Passes? by Eric Lippert that is a compiler pass. To quote:

Then we run an optimization pass that rewrites trivial "is" and "as" operators.

So perhaps that is why you are seeing the same CIL generated for both snippets.

The scope of 'y' is reduce if you place the declaration inside the loop.

Whoever wrote it probably prefers casting 'x as T' more than '(T)x', and wanted to limit the scope of 'y'.

You forgot about value types. Eg:

    static void Main(string[] args)
    {
        ValueType vt;
        FooClass f = vt as FooClass;

    }

    private class FooClass
    {
        public int Bar { get; set; }
    }

Won't compile as value types can't be converted like this.

MaLio

I suspect strongly that is is faster than as and does not require an allocation. So if x is rarely ever Bar, then the first snippet is good. If x is mostly Bar, then an as would be recommended, as no second cast is required. It depends on the usage and circumstances of the code.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!