2012-02-15 23 views
10

Ho il seguente progetto C# rivolto a .NET 4.0 che accetta un file di codice sorgente, lo compila in un assembly al volo e quindi esegue un metodo statico di un tipo contenuto in tale assembly.Perché il codice C# compilato al volo non funziona quando un debugger è collegato?

Questo funziona come previsto, a condizione che non avvii il programma con un debugger collegato. In tal caso ricevo un'eccezione sulla chiamata a xmlSerializer.Serialize(sw, family);, più precisamente a System.NullReferenceException all'interno di System.TypeInitializationException all'interno di System.InvalidOperationException.

Se prendo lo stesso programma, includo il file del codice sorgente nel progetto e lo compilo direttamente nell'assembly del programma principale, non otterrò un'eccezione indipendentemente dal fatto che sia collegato o meno un debugger.

Si noti che il mio progetto fa riferimento agli stessi assemblaggi identici a quelli elencati durante la compilazione al volo.

Perché è importante per il codice compilato al volo se è collegato o meno un debugger? Cosa mi manca?

lima principale Program.cs:

using System; 
using System.CodeDom.Compiler; 
using System.IO; 
using System.Reflection; 
using System.Linq; 

namespace DebugSerializeCompiler 
{ 
    class Program 
    { 
     static void Main() 
     { 
      if (!Environment.GetCommandLineArgs().Contains("Compile")) 
      { 
       DebugSerializeCompiler.SerializerTest.Run(); 
      } 
      else 
      { 
       Assembly assembly; 
       if (TryCompile("..\\..\\SerializerTest.cs", new[]{ "Microsoft.CSharp.dll", 
        "System.dll", "System.Core.dll", "System.Data.dll", "System.Xml.dll" }, 
        out assembly)) 
       { 
        Type type = assembly.GetType("DebugSerializeCompiler.SerializerTest"); 
        MethodInfo methodInfo = type.GetMethod("Run"); 
        methodInfo.Invoke(null, null); 
       } 
      } 
      Console.ReadKey(); 
     } 

     static bool TryCompile(string fileName, string[] referencedAssemblies, 
      out Assembly assembly) 
     { 
      bool result; 

      CodeDomProvider compiler = CodeDomProvider.CreateProvider("CSharp"); 
      var compilerparams = new CompilerParameters 
            { 
             GenerateExecutable = false, 
             GenerateInMemory = true 
            }; 
      foreach (var referencedAssembly in referencedAssemblies) 
      { 
       compilerparams.ReferencedAssemblies.Add(referencedAssembly); 
      } 

      using (var reader = new StreamReader(fileName)) 
      { 
       CompilerResults compilerResults = 
        compiler.CompileAssemblyFromSource(compilerparams, reader.ReadToEnd()); 
       assembly = compilerResults.CompiledAssembly; 
       result = !compilerResults.Errors.HasErrors; 
       if (!result) 
       { 
        Console.Out.WriteLine("Compiler Errors:"); 
        foreach (CompilerError error in compilerResults.Errors) 
        { 
         Console.Out.WriteLine("Position {0}.{1}: {2}", 
          error.Line, error.Column, error.ErrorText); 
        } 
       } 
      } 

      return result; 
     } 
    } 
} 

file compilato in assembly separato SerializerTest.cs:

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Xml.Serialization; 

namespace DebugSerializeCompiler 
{ 
    public class SerializerTest 
    { 
     public static void Run() 
     { 
      Console.WriteLine("Executing Run()"); 
      var family = new Family(); 
      var xmlSerializer = new XmlSerializer(typeof(Family)); 

      TextWriter sw = new StringWriter(); 
      try 
      { 
       if (sw == null) Console.WriteLine("sw == null"); 
       if (family == null) Console.WriteLine("family == null"); 
       if (xmlSerializer == null) Console.WriteLine("xmlSerializer == null"); 
       xmlSerializer.Serialize(sw, family); 
      } 
      catch (Exception e) 
      { 
       Console.WriteLine("Exception caught:"); 
       Console.WriteLine(e); 
      } 
      Console.WriteLine(sw); 
     } 
    } 

    [Serializable] 
    public class Family 
    { 
     public string LastName { get; set; } 

     public List<FamilyMember> FamilyMembers { get; set; } 
    } 

    [Serializable] 
    public class FamilyMember 
    { 
     public string FirstName { get; set; } 
    } 
} 

Questo è il file csproj utilizzato per compilare il progetto utilizzando Visual C# 2010 Express su Windows 7:

<?xml version="1.0" encoding="utf-8"?> 
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 
    <PropertyGroup> 
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> 
    <Platform Condition=" '$(Platform)' == '' ">x86</Platform> 
    <ProductVersion>8.0.30703</ProductVersion> 
    <SchemaVersion>2.0</SchemaVersion> 
    <ProjectGuid>{7B8D2187-4C58-4310-AC69-9F87107C25AA}</ProjectGuid> 
    <OutputType>Exe</OutputType> 
    <AppDesignerFolder>Properties</AppDesignerFolder> 
    <RootNamespace>DebugSerializeCompiler</RootNamespace> 
    <AssemblyName>DebugSerializeCompiler</AssemblyName> 
    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> 
    <TargetFrameworkProfile>Client</TargetFrameworkProfile> 
    <FileAlignment>512</FileAlignment> 
    </PropertyGroup> 
    <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> 
    <PlatformTarget>x86</PlatformTarget> 
    <DebugSymbols>true</DebugSymbols> 
    <DebugType>full</DebugType> 
    <Optimize>false</Optimize> 
    <OutputPath>bin\Debug\</OutputPath> 
    <DefineConstants>DEBUG;TRACE</DefineConstants> 
    <ErrorReport>prompt</ErrorReport> 
    <WarningLevel>4</WarningLevel> 
    </PropertyGroup> 
    <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> 
    <PlatformTarget>x86</PlatformTarget> 
    <DebugType>pdbonly</DebugType> 
    <Optimize>true</Optimize> 
    <OutputPath>bin\Release\</OutputPath> 
    <DefineConstants>TRACE</DefineConstants> 
    <ErrorReport>prompt</ErrorReport> 
    <WarningLevel>4</WarningLevel> 
    </PropertyGroup> 
    <ItemGroup> 
    <Reference Include="System" /> 
    <Reference Include="System.Core" /> 
    <Reference Include="Microsoft.CSharp" /> 
    <Reference Include="System.Data" /> 
    <Reference Include="System.Xml" /> 
    </ItemGroup> 
    <ItemGroup> 
    <Compile Include="Program.cs" /> 
    <Compile Include="Properties\AssemblyInfo.cs" /> 
    <Compile Include="SerializerTest.cs"> 
     <SubType>Code</SubType> 
    </Compile> 
    </ItemGroup> 
    <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> 
    <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
     Other similar extension points exist, see Microsoft.Common.targets. 
    <Target Name="BeforeBuild"> 
    </Target> 
    <Target Name="AfterBuild"> 
    </Target> 
    --> 
</Project> 
+0

Quindi quale oggetto è nullo? –

+0

@Ramhound Buona domanda. Certamente nessuno dei miei oggetti è nullo. Ho appena aggiunto un assegno per chiarirlo. – PersonalNexus

+0

Puoi anche provare a utilizzare l'anteprima di Roslyn e vedere se hai più fortuna rispetto al CodeDom. Ci sono altri strumenti che fanno cose simili, come il compilatore mono come server e NRefactory ... La codicosità fa schifo. –

risposta

2

Ha funzionato bene per me.

Ma se dovessi indovinare cosa sta succedendo per te sarebbe perché, compilando la classe con il tuo progetto principale e compilandolo dinamicamente, il serializzatore si confonde su quale assembly usare e stia fallendo. Puoi provare ad allegare un evento a AppDomain.CurrentDomain.AssemblyResolve e vedere se ci sono degli assembly che non riescono a risolvere lì.

+0

Che IDE e versione stai usando? Che versione del framework è questa? Sto usando VS 2008 SP1 su win7 e il tuo codice è in esecuzione compilato come è senza problemi. Funziona allo stesso modo per me con o senza il debugger allegato. Avete estensioni, come R #? Puoi mostrare il file .csproj? Potresti avere qualcosa di strano in corso nel modo in cui stai compilando il progetto. –

+0

Questo è tutto. Nel gestore di eventi 'AppDomain.CurrentAppDomain.AssemblyResolve' ottengo una richiesta per l'assembly compilato al volo, ma solo quando è in esecuzione nel debugger. Quando salvi l'assembly in un campo '_assembly' dopo la compilazione e lo restituisco quando il nome dell'assembly richiesto corrisponde al nome dell'assembly compilato nel gestore eventi, funziona anche con il debugger allegato. Mi piacerebbe davvero sapere perché questo faccia la differenza, però. – PersonalNexus

+0

È .NET 4.0 e l'ho provato con Visual Studio 2010 Premium e Visual Studio 2010 Express Edition (senza estensioni) con un progetto di applicazione console standard e senza modifiche alle impostazioni IDE o debugger. Includerò il file .csproj nella domanda. – PersonalNexus

Problemi correlati