首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >.NET内核6中的动态编译

.NET内核6中的动态编译
EN

Stack Overflow用户
提问于 2022-03-14 22:29:00
回答 1查看 1.6K关注 0票数 1

我一直在试验C#动态编译,如Laurent的优秀博客:https://laurentkempe.com/2019/02/18/dynamically-compile-and-run-code-using-dotNET-Core-3.0/ (Merci!)

我将代码复制并粘贴到一个文件中,所有这些都是为了更好地理解控制流的主要方法。

但是,我一直无法弄清楚为什么DLL的卸载总是失败(即WeakReference仍然处于活动状态)。洛朗的代码(发布在GitHub上)卸载DLL,而我的复制粘贴的单块代码不卸载。

有人能帮我找出我哪里出了问题吗?

代码语言:javascript
复制
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;

namespace CoreCompile
{
   public class CompilerTest
   {
      public static void Main(string[] args)
      {
         Console.WriteLine("Hello, World!");

         string sourcePath = args.Length > 0 ? args[0] : @"D:\DynamicRun\Sources\DynamicProgram.cs";
         string sourceCode = File.ReadAllText(sourcePath);
         string assemblyPath = Path.ChangeExtension(Path.GetFileNameWithoutExtension(sourcePath), "DLL");

         var codeString = SourceText.From(sourceCode);
         var options = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp10);

         var parsedSyntaxTree = SyntaxFactory.ParseSyntaxTree(codeString, options);

         var references = new List<MetadataReference>
            {
                MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(Console).Assembly.Location)
            };

         Assembly.GetEntryAssembly()?.GetReferencedAssemblies().ToList()
             .ForEach(a => references.Add(MetadataReference.CreateFromFile(Assembly.Load(a).Location)));

         var csCompilation = CSharpCompilation.Create(assemblyPath,
             new[] { parsedSyntaxTree },
             references: references,
             options: new CSharpCompilationOptions(OutputKind.ConsoleApplication,
                 optimizationLevel: OptimizationLevel.Release,
                 assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default));

         WeakReference assemblyLoadContextWeakRef = null;
         using (var peStream = new MemoryStream())
         {
            var result = csCompilation.Emit(peStream);

            if (result.Success)
            {
               Console.WriteLine("Compilation done without any error.");
               peStream.Seek(0, SeekOrigin.Begin);
               var compiledAssembly = peStream.ToArray();
               string[] arguments = new[] { "France" };

               using (var asm = new MemoryStream(compiledAssembly))
               {
                  var assemblyLoadContext = new SimpleUnloadableAssemblyLoadContext();
                  var assembly = assemblyLoadContext.LoadFromStream(asm);
                  var entry = assembly.EntryPoint;
                  _ = entry != null && entry.GetParameters().Length > 0
                      ? entry.Invoke(null, new object[] { arguments })
                      : entry.Invoke(null, null);

                  assemblyLoadContext.Unload();
                  assemblyLoadContextWeakRef = new WeakReference(assemblyLoadContext);
               } // using
            }
            else
            {
               Console.WriteLine("Compilation done with error.");
               var failures = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error);
               foreach (var diagnostic in failures)
               {
                  Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
               }
            }
         } // using

         if (assemblyLoadContextWeakRef != null)
         {
            for (var i = 0; i < 8 && assemblyLoadContextWeakRef.IsAlive; i++)
            {
               GC.Collect();
               GC.WaitForPendingFinalizers();
            }
            Console.WriteLine(assemblyLoadContextWeakRef.IsAlive ? "Unloading failed!" : "Unloading success!");
         }

      } // Main
   } // class

   internal class SimpleUnloadableAssemblyLoadContext : AssemblyLoadContext
   {
      public SimpleUnloadableAssemblyLoadContext()
          : base(true)
      {
      }

      protected override Assembly Load(AssemblyName assemblyName)
      {
         return null;
      }
   }


} // namespace
EN

回答 1

Stack Overflow用户

发布于 2022-03-15 03:40:27

强迫物体被垃圾收集是一种黑暗的艺术。您需要确保垃圾收集器不能定位Main方法中的任何变量,否则对象将保持活动状态。

例如,调试构建和发行版构建将行为不同,因为版本构建将在不再需要变量时立即丢弃这些变量。

从您的示例中,您的局部变量assemblyLoadContext仍然在作用域内,特别是在调试版本中。因为您可能会在方法的末尾放置一个断点,以便检查任何局部变量。

也许您能做的最简单的事情就是将大部分代码移到单独的方法中。一旦该方法返回,所有局部变量都应超出作用域,垃圾收集器无法检测。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71474900

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档