Question

Consider the follow code sample (copy-pastable in LINQPad):

void Main()
{
    var ttNamespace = new CodeNamespace("SampleNamespace");
    ttNamespace.Imports.AddRange(new[]{"System", "System.Text", "System.Threading"}.Select(x => new CodeNamespaceImport(x)).ToArray());

    var newType = new CodeTypeDeclaration("SampleClass")
    {
        TypeAttributes = TypeAttributes.Public
    };

    var newField = new CodeMemberField(typeof(List<int>), "_field")
    {
        Attributes = MemberAttributes.Public
    };

    newField.InitExpression = new CodeObjectCreateExpression(new CodeTypeReference(typeof(List<int>)), new CodeExpression[] { });

    newType.Members.Add(newField);
    ttNamespace.Types.Add(newType);

    var parameters = new CompilerParameters()
    {
        GenerateExecutable = false,
        OutputAssembly = @"C:\test.dll"
    };

    var ccUnit = new CodeCompileUnit();
    ccUnit.Namespaces.Add(ttNamespace);

    var result = CodeDomProvider.CreateProvider("C#").CompileAssemblyFromDom(parameters, ccUnit);

    Console.WriteLine (result.Errors);
}

As you can see, I explicitly add namespaces System, System.Text and System.Threading. I also utilize the type List<int> but don't provide a namespace import for System.Collections.Generic.

Logically this should result in an error somewhere down the line. Maybe not at compile time, but definitely at runtime. This is not the case though, the code compiles to this:

enter image description here

We can see that all using statements have already been organized for us: redundant ones are left out and required ones are added. Obviously this is very useful but this begs the question:

What is the point of explicit CodeNamespaceImport statements when the compiler doesn't use it anyway?

I should note that completely omitting the adding of my namespaces doesn't change the output at all: it keeps working as it is.

Was it helpful?

Solution

There is no point, typeof(List<int>) is unambiguous in your program and can generate the full type name of List. It isn't very clear where you got the source listing from, I suspect you used a disassembler that automatically figured out which using directives where most useful.

You can modify the program like this to see what the compiler really sees:

        var prov = CodeDomProvider.CreateProvider("C#");
        var gen = prov.CreateGenerator();
        using (var writer = new System.IO.StreamWriter("c:\\temp\\test.cs")) {
            gen.GenerateCodeFromCompileUnit(ccUnit, writer, new CodeGeneratorOptions());
        }

        var result = prov.CompileAssemblyFromDom(parameters, ccUnit);

Which produces:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.34011
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace SampleNamespace {
    using System;
    using System.Text;
    using System.Threading;


    public class SampleClass {

        public System.Collections.Generic.List<int> _field = new System.Collections.Generic.List<int>();
    }
}

As you can tell, the compiler actually sees the full type name and the using directives are not necessary.

OTHER TIPS

At the IL level, there are no namespace imports, all types are always referenced using their full name.

So, when a decompiler generates C# from IL, it doesn't know what namespace imports did the original code contain. This means that a reasonable decompiler will probably generate code that imports all namespaces that are used in a class (unless that would cause conflicts).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top