Why does this syntactically invalid C# code compile? - c#

I discovered this quite by accident, but I had apparently written some incorrect code in a much larger project. I've reduced it to an MVCE, and am curious why it even compiles. I accidentally added what the system appears to be treating as a variable name after a type check in C#, and it appears to be treating as an alias to the variable I was type checking. You can test in the dotnetfiddle below.
Does anyone have an explanation for what on earth is going on here?
For reference I'm using vs2017 enterprise with a .net 4.6.1 project.
using System;
public class Program
{
public static void Main()
{
string foo = "hello world";
if (!(foo is string str)) { return; }
Console.WriteLine(str);
}
}
https://dotnetfiddle.net/BwT1kx

Not a bug, but a feature! that's the pattern matching supported by the is keyword since C# 7.0
For some reason, your IDE is using the wrong C# version for analysing the code and giving you incorrect squiggly lines.

This is a feature called pattern matching as others pointed starting from C#. If you are interested in what compiler translates this to you can check compiler generated code.
Notice how in Release mode compiler makes static analysis that foo is always a string type and omits most of the generated code in Debug.

Related

Why doesn't Visual Studio warn about a circular initialisation of a static string?

A silly but probably not-so-uncommon error when I replaced a string literal by a static string was leading to something like the following code snippet which is probably almost never intended and trivial to spot, even for a machine:
public partial class T { static string S = S; }
This doesn't trigger a diagnostic with VS Professional 2013, .NET Framework 4.6.0.1055, Warning level 4.1 Why not?
1To be fair with the compiler I assume that the initialization is well defined and assigns S' value in its default value state prior to the explicit initialization, i.e. null, to itself when the initialization is eventually performed; but it is almost certainly a coding error.
In recent versions of Visual Studio it says:
Warning CS1717 Assignment made to same variable; did you mean to assign something else?
So... update your IDE :)

How to translate or convert CompilerGenerated code?

If you try to use decompilers like: jetbrains dotpeek, redgate reflector, telerik justdecompile, whatever.. Sometimes if you need a code to copy or just to understand, it is not possible because are shown somethings like it:
[CompilerGenerated]
private sealed class Class15
{
// Fields
public Class11.Class12 CS$<>8__locals25;
public string endName;
// Methods
public Class15();
public bool <Show>b__11(object intelliListItem_0);
}
I'm not taking about obfuscation, this is happens at any time, I didsome tests (my own code), and occurs using lambdas and iterators. I'm not sure, could anyone give more information about when and why..?
So, by standard Visual Studio not compile $ and <> keywords in c# (like the code above)...
There is a way to translate or convert this decompiled code automatically?
Lambdas are a form of closure which is a posh way of saying it's a unit of code you can pass around like it was an object (but with access to its original context). When the compiler finds a lambda it generates a new type (Type being a class or struct) which encapsulates the code and any fields accessed by the lambda in its original context.
The problem here is, how do you generate code which will never conflict with user written code?
The compiler's answer is to generate code which is illegal in the language you are using, but legal in IL. IL is "Intermediate Language" it's the native language used by the Common Language Runtime. Any language which runs on the CLR (C#, vb.net, F#) compiles into IL. This is how you get to use VB.Net assemblies in C# code and so on.
So this is why the decompilers generate the hideous code you see. Iterators follow the exact same model as do a bunch of other language features that require generated types.
There is an interesting side effect. The Lambda may capture a variable in its original context:
public void TestCapture()
{
StringBuilder b = new StringBuilder();
Action l = () => b.Append("Kitties!");
}
So by capture I mean the variable b here is included in the package that defines the closure.
The compiler tries to be efficient and create as few types as possible, so you can end up with one generated class that supports all the lambdas found in a specific class, including fields for all the captured variables. In this way, if you're not careful, you can accidentally capture something you expect to be released, causing really tricky to trace memory leaks.
Is there an option to change the target framework?... I know with some decompilers they default to the lowest level framework (C# 1.0)

C# why code compiles without any name spaces included

I tried the following
// no using statements here, not even using System;
class Program
{
static void Main(string[] args)
{
string[] arr = new string[3] { "A" , "B", "C" };
}
}
My question is simple, even without any using statements, how come compiler is able to compile this, more specifically, shouldn't there be a compile time error because I am trying to build an array and System.Array is not accessible?
Thanks.
You seem to have a misunderstanding about what using means. A using directive is for determining what a simple name means in your program at compile time. When you say using Foo; that means "if the text of this program uses the simple name Bar, and it doesn't seem like anything named Bar is in scope where the name is used then try checking to see if there is a type called Bar inside namespace Foo".
Your program does not contain any simple names! (string is not a simple name; it is a keyword of the language.) Therefore there is no need to have any using directive to resolve the non-existing simple names.
using does not at all mean "this program uses features that require types found in this assembly", which is what you seem to think it means. using directives are only about (1) simple name resolution, and (2) extension method name resolution.
Arrays are part of the language. You don't need to reference a namespace to use them.
string[] will translate to System.Array while compiling. Since its already fully qualified, it doesn't need any using statements.
You're not referring by name to any namespaced type but yours (the Program class).
string and [] are aliases that are indeed equivalent to System.String and System.Array respectively, but using them does not require pulling their counterparts' namespaces into scope.
You aren't directly referencing the Array class or its members, so the compiler won't complain that you're trying to reference an unused namespace or type. You're just using notation that's part of the C# language itself to make arrays.
This is because you are using core things, you don't even need System for this to compile. The System namespace has extended functionality, some of the classes included in the System Namespace is:
Action
Func
Console
And many more..
As you are not trying to write something to the command prompt, creating actions or even asking for date using DateTime, you don't need to import anything.
using statements do not include anything or provide any ekstra code. It only makes it simplier to code, because you don't have to write the whole path every time.
string automatically converts to System.String.
You can write this:
class Foo {
string Bar() {
return System.IO.File.Exists("path").ToString();
}
}
or just
using System.IO;
class Foo {
string Bar() {
return File.Exists("path").ToString();
}
}
both compiles to the same program.
Namespaces are not necessary, you can organize with namespaces your code but you don't have to.
And common keywords are automatically referenced to the proper namespace.
For better understanding read this msdna document http://msdn.microsoft.com/en-us/library/ms973231.aspx

C# compiler bug? Object initializer syntax used for write-only property in Expression makes csc crash

You may consider this a bug report, however I'm curious if I am terribly wrong here, or if there is an explanation from Eric or someone else at Microsoft.
Update
This is now posted as a bug on Microsoft Connect.
Description
Consider the following class:
class A
{
public object B {
set { }
}
}
Here, A.B is a write-only but otherwise fine property.
Now, imagine we assign it inside of expression:
Expression<Func<A>> expr =
() => new A {
B = new object { }
};
This code makes C# compiler (both 3.5.30729.4926 and 4.0.30319.1) spit out
Internal Compiler Error (0xc0000005 at address 013E213F): likely culprit is 'BIND'.
and crash.
However, merely replacing object initializer syntax ({ }) with a constructor (( )) compiles just fine.
Full code for reproduction:
using System;
using System.Linq.Expressions;
class Test {
public static void Main()
{
Expression<Func<A>> expr =
() => new A {
B = new object { }
};
}
}
class A {
public object B { set { } }
}
(And yes, I did hit it working on a real project.)
I'm afraid I'm not Eric Lippert (Oh, but could I be so dashing...), but as a former Visual Studio languages guy who can still search the sources, I can say two things about this:
Any time you see something that starts with "Internal Compiler Error" you have most definitely found a bug. That's what that error exists for, whether it's the C#, VB or C++ compiler. It's the "Oh, s**t, something just went really unexpectedly wrong!" throw-up-our-hands-and-bail-out error.
Beyond that, this is definitely a bug in the C# compiler that should be reported. The code that's crashing is assuming that when you're doing an initializer on a property that there's a getter it can look at and, hey, guess what? In this case, there isn't. Oddly enough, if I change the type being constructed to some type "C" instead of "object", I don't get the crash, so I'm guessing it's a failure further up the stack (i.e. the code never should have gotten down to the point where it was looking for the property getter).
Hope this helps.
This is what I found online related to the error,
Posted by Microsoft on 3/9/2010 at 10:58 AM
Thanks everyone for the reports. I
believe that this issue has been fixed
post-RC. The problem is that the C#
compiler is crashing as it is tries to
report an error or warning. In several
cases we have seen the warning being
reported is that the LIB environment
variable contains an invalid path. To
avoid the crash, check that your LIB
environment variable contains valid
paths.
Regards,
Ed Maurer Development Manager, VB and
C# compilers

Implicit delegate conversion not always working for delegates in another project? [duplicate]

UPDATE: I've filed this as an issue on Microsoft Connect if you can reproduce this and/or would love to see this fixed please help vote up the issue over there.
I've been trying to solve this problem for hours now.
Would really appreciate whatever idea/advice you can think of.
First of all, I have 3 files Class.cs Definitions.cs and Program.cs. I've pasted file contents over at http://pastie.org/1049492 for you to try out.
The problem is that, If you have ALL 3 files in the same console application project. The application compiles and runs just fine.
If however, I have Class.cs and Definitions.cs in a "library" project which is referenced to from the main console application project which has only the Program.cs file, compilation fails with:
Delegate Act does not take 2 arguments.
Cannot convert lambda expression to delegate type 'DC.Lib.Produce' because some of the return types in the block are not implicitly convertible to the delegate return type ...
Here is a complete solution with 3 projects -- 1 with all files combined together and another with the definitions put in another project:
http://dl.dropbox.com/u/149124/DummyConsole.zip
I'm using VS2010 RTW Professional edition.
Interesting. I think you've found an actual bug in the C# compiler - although I may be missing something subtle. I've written a slightly simplified version which avoids possibilities of overloading etc coming into play, and which dispenses with the extra method:
// Definitions.cs
public interface IData { }
public delegate IData Foo(IData input);
public delegate IData Bar<T>(IData input, T extraInfo);
public delegate Foo Produce<T>(Bar<T> next);
// Test.cs
class Test
{
static void Main()
{
Produce<string> produce =
next => input => next(input, "This string should appear.");
}
}
Demonstration of compiling as one assembly, with no errors:
> csc Test.cs Definitions.cs
Demonstration of compiling as two assemblies with errors:
> csc /target:library Definitions.cs
> csc Test.cs /r:Definitions.dll
Test.cs(5,43): error CS1662: Cannot convert lambda expression
to delegate type 'Produce<string>'
because some of the return types in the block are not
implicitly convertible to the delegate return type
Test.cs(5,52): error CS1593: Delegate 'Bar' does not take 2 arguments
I can't think of any reason why this should be different across different assemblies, as everything's public. The spec rarely talks about assembly boundaries other than for internal reasons.
Interestingly, I get the same error for both the C# 3 and 4 compilers.
Emailing Eric and Mads now...
EDIT: Note that you can work around this using an explicit parameter list. For example, in my sample code, this will work:
Produce<string> produce =
(Bar<string> next) => input => next(input, "This string should appear.");
I resolved this by renaming of
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.CSharp.targets (don't delete it!)
which is a warning in output window. The new project wroks fine now but the previous one couldn't be loaded. After that I renamed the file again to it's original name. Now both projects can be compiled without errors. This is an empiric solution but I hope it helps

Categories

Resources