我一直在试验C#动态编译,如Laurent的优秀博客:https://laurentkempe.com/2019/02/18/dynamically-compile-and-run-code-using-dotNET-Core-3.0/ (Merci!)
我将代码复制并粘贴到一个文件中,所有这些都是为了更好地理解控制流的主要方法。
但是,我一直无法弄清楚为什么DLL的卸载总是失败(即WeakReference仍然处于活动状态)。洛朗的代码(发布在GitHub上)卸载DLL,而我的复制粘贴的单块代码不卸载。
有人能帮我找出我哪里出了问题吗?
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发布于 2022-03-15 03:40:27
强迫物体被垃圾收集是一种黑暗的艺术。您需要确保垃圾收集器不能定位Main方法中的任何变量,否则对象将保持活动状态。
例如,调试构建和发行版构建将行为不同,因为版本构建将在不再需要变量时立即丢弃这些变量。
从您的示例中,您的局部变量assemblyLoadContext仍然在作用域内,特别是在调试版本中。因为您可能会在方法的末尾放置一个断点,以便检查任何局部变量。
也许您能做的最简单的事情就是将大部分代码移到单独的方法中。一旦该方法返回,所有局部变量都应超出作用域,垃圾收集器无法检测。
https://stackoverflow.com/questions/71474900
复制相似问题