C# Roslyn .NET CORE 3.1 CSharpCompilation 动态 RuntimeBinder.Binder.Convert 错误

C# Roslyn .NET CORE 3.1 CSharpCompilation dynamic RuntimeBinder.Binder.Convert Error

使用 .NET Core 3.1 (Windows) 我正在尝试动态编译使用 dynamic 类型的 class。我继续收到错误消息:

error CS0656: Missing compiler required member 'Microsoft.CSharp.RuntimeBinder.Binder.Convert'

我已经添加了对 Microsoft.CSharp 和 System.Linq.Expressions 的引用,但仍然缺少我无法找到的程序集引用。

我已经浏览了我可以在 Whosebug 上找到的所有在线参考资料,并且 github 解决了这个错误。

一个有趣的提示,这在 .NET Core 5.0 下工作......不幸的是我还没有准备好升级到 5。

    class Program
    {
        static void Main(string[] args)
        {
            var code = @"
using System;
using System.Collections.Generic;

namespace Debuggable
{
    public class HelloWorld
    {
        public string DoWork()
        {
            var a = ""foo"";

            var map = new Dictionary<string, object>();
            map[""a""] = ""bar"";
    
            if (map.TryGetValue(""a"", out dynamic t))
                a = t;

            return a;
        }
    }
}
            ";

            (byte[] a, byte[] b) = CreateAssembly(code);
            var assembly = Assembly.Load(a, b);

            dynamic instance = assembly.CreateInstance("Debuggable.HelloWorld");
            string result = instance.DoWork();

            Console.WriteLine(result);
        }

        public static (byte[], byte[]) CreateAssembly(string code)
        {
            var encoding = Encoding.UTF8;
            var assemblyName = Path.GetRandomFileName();
            var symbolsName = Path.ChangeExtension(assemblyName, "pdb");

            var references = new MetadataReference[]
            {
                MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location),
                MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("Microsoft.CSharp")).Location),
                MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("System")).Location),
                MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("System.Runtime")).Location),
                MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("System.Collections")).Location),
                MetadataReference.CreateFromFile(typeof(System.Linq.Expressions.ExpressionType).GetTypeInfo().Assembly.Location),
            };

            var syntaxTrees = new List<SyntaxTree>();
            var embeddedTexts = new List<EmbeddedText>();

            var sourceCodePath = "generated.cs";
            var buffer = encoding.GetBytes(code);
            var sourceText = SourceText.From(buffer, buffer.Length, encoding, canBeEmbedded: true);

            var syntaxTree = CSharpSyntaxTree.ParseText(
                sourceText,
                new CSharpParseOptions().WithLanguageVersion(LanguageVersion.CSharp8),
                path: sourceCodePath);

            var syntaxRootNode = syntaxTree.GetRoot() as CSharpSyntaxNode;
            var encoded = CSharpSyntaxTree.Create(syntaxRootNode, null, sourceCodePath, encoding);

            syntaxTrees.Add(encoded);
            embeddedTexts.Add(EmbeddedText.FromSource(sourceCodePath, sourceText));

            CSharpCompilation compilation = CSharpCompilation.Create(
                assemblyName,
                syntaxTrees: syntaxTrees,
                references: references,
                options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
                    .WithOptimizationLevel(OptimizationLevel.Debug)
                    .WithPlatform(Platform.AnyCpu)
            );

            using (var assemblyStream = new MemoryStream())
            using (var symbolsStream = new MemoryStream())
            {
                var emitOptions = new EmitOptions(
                    debugInformationFormat: DebugInformationFormat.PortablePdb,
                    pdbFilePath: symbolsName);

                EmitResult result = compilation.Emit(
                    peStream: assemblyStream,
                    pdbStream: symbolsStream,
                    embeddedTexts: embeddedTexts,
                    options: emitOptions);

                if (!result.Success)
                {
                    var errors = new List<string>();

                    IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
                        diagnostic.IsWarningAsError ||
                        diagnostic.Severity == DiagnosticSeverity.Error);

                    foreach (Diagnostic diagnostic in failures)
                        errors.Add($"{diagnostic.Id}: {diagnostic.GetMessage()}");

                    throw new Exception(string.Join("\n", errors));
                }

                return (assemblyStream.GetBuffer(), symbolsStream.GetBuffer());
            }
        }
    }

我能够使用 MSBuild 添加的引用缩小范围。如果其他人在 .NET Core 3.1 中遇到这个问题,他们会在这里。

MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("Microsoft.CSharp")).Location),
MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("netstandard")).Location),
MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("System.Linq.Expressions")).Location),
MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("System.Private.CoreLib")).Location),
MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("System.Runtime")).Location),

此外,我注意到如果通过显式文件路径加载程序集,则所需引用集会有所不同。区别在于 \dotnet\packs\dotnet\shared 程序集。我对此了解不多,无法说明原因。