【问题标题】:Roslyn - Use CSharpCompilation to compile assembly to be used in another program compiled by CSharpCompilationRoslyn - 使用 CSharpCompilation 编译程序集以在 CSharpCompilation 编译的另一个程序中使用
【发布时间】:2020-10-12 21:02:12
【问题描述】:

我正在尝试使用 CSharpCompilation 编译一个程序集,其中包含一个简单的类,然后我可以在另一个也通过 CSharpCompilation 编译的程序中引用它。我有这个代码:

命名空间编译测试
{
    课堂节目
    {
        静态无效主要(字符串 [] 参数)
        {
            HashSet 引用的Assemblies = new HashSet()
            {
                typeof(object).Assembly,
                Assembly.Load(new AssemblyName("Microsoft.CSharp")),
                Assembly.Load(new AssemblyName("netstandard")),
                Assembly.Load(new AssemblyName("System.Runtime")),
                Assembly.Load(new AssemblyName("System.Linq")),
                Assembly.Load(new AssemblyName("System.Linq.Expressions"))
            };
            
            字符串问候语 = @"
命名空间 TestLibraryAssembly
{
    公共静态类问候语
    {
        公共静态字符串 GetGreeting(字符串名称)
        {
            返回“”你好,“”+名称+“”!“”;
        }
    }
}
";
            
            CSharpCompilation 编译1 = CSharpCompilation.Create(
                "TestLibraryAssembly",
                新的 []
                {
                    CSharpSyntaxTree.ParseText(greetingClass)
                },
                referencedAssemblies.Select(assembly => MetadataReference.CreateFromFile(assembly.Location)).ToList(),
                新 CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
            );
            MemoryStream memoryStream1 = new MemoryStream();
            EmitResult emitResult1 = 编译1.Emit(memoryStream1);
            memoryStream1.Position = 0;
            MetadataReference testLibraryReference = MetadataReference.CreateFromStream(memoryStream1);

            字符串程序代码 = @"
使用 TestLibraryAssembly;

命名空间测试程序
{
    公开课程序
    {
        公共无效主要()
        {
            字符串问候 = Greeting.GetGreeting(""Name"");
        }
    }
}
";
            
            CSharpCompilation 编译2 = CSharpCompilation.Create(
                "测试程序",
                新的 []
                {
                    CSharpSyntaxTree.ParseText(programCode)
                },
                引用的程序集
                    .Select(assembly => MetadataReference.CreateFromFile(assembly.Location))
                    .Concat(new List { testLibraryReference }).ToList(),
                新 CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
            );
            MemoryStream memoryStream2 = new MemoryStream();
            EmitResult emitResult2 = 编译2.Emit(memoryStream2);
            memoryStream2.Position = 0;
            
            汇编程序Assembly = Assembly.Load(memoryStream2.ToArray());
            类型 programType = programAssembly.GetType("TestProgram.Program");
            MethodInfo 方法 = programType.GetMethod("Main");
            对象实例 = Activator.CreateInstance(programType);
            方法。调用(实例,空);
        }
    }
}

但是,当我运行它时,我得到了这个错误:

未处理的异常。 System.Reflection.TargetInvocationException:调用的目标已引发异常。

---> System.IO.FileNotFoundException:无法加载文件或程序集“TestLibraryAssembly,Version=0.0.0.0,Culture=neutral,PublicKeyToken=null”。系统找不到指定的文件。

文件名:'TestLibraryAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'

我该如何解决这个错误?

【问题讨论】:

    标签: c# .net-core .net-assembly roslyn


    【解决方案1】:

    您的示例中的问题是激活器 (Activator.CreateInstance(programType);) 如何尝试解决依赖关系。它会在磁盘上查找文件。

    解决此问题的一种方法是将文件保存在磁盘上,然后使用Activator.CreateInstanceFrom 创建实例。

    您可以在这里找到一个正在执行此操作的 sn-p:

    using System;
    using System.IO;
    using Microsoft.CodeAnalysis;
    using Microsoft.CodeAnalysis.CSharp;
    
    namespace Playground
    {
        public static class Program
        {
            private const string firstClass =
    @"
    namespace A
    {
        public class Foo
        {
            public int Bar() => 21;
        }
    }";
    
            private const string secondClass =
    @"using A;
    
    namespace B
    {
        public class Test
        {
            public int GetValue() => new Foo().Bar();
        }
    }
    ";
    
            public static void Main()
            {
                var firstAssemblyFileName = Path.Combine(Path.GetTempPath(), "A.dll");
                var secondAssemblyFileName = Path.Combine(Path.GetTempPath(), "B.dll");
    
                var compilation = CreateCompilation(CSharpSyntaxTree.ParseText(firstClass), "A");
                var secondCompilation = CreateCompilation(CSharpSyntaxTree.ParseText(secondClass), "B")
                    .AddReferences(compilation.ToMetadataReference());
    
                compilation.Emit(firstAssemblyFileName);
                secondCompilation.Emit(secondAssemblyFileName);
    
                dynamic testType = Activator.CreateInstanceFrom(secondAssemblyFileName, "B.Test").Unwrap();
                var value = testType.GetValue();
            }
    
            private static CSharpCompilation CreateCompilation(SyntaxTree tree, string name) =>
                CSharpCompilation
                    .Create(name, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
                    .AddReferences(MetadataReference.CreateFromFile(typeof(string).Assembly.Location))
                    .AddSyntaxTrees(tree);
        }
    }
    

    【讨论】:

      【解决方案2】:

      由于您使用的是netstandard,因此没有引用System.IO

      将此行添加到referencedAssembliesHashSet 声明部分:

      Assembly.Load("System.IO.FileSystem")
      

      已编辑:

      或者更安全的使用:

      typeof(File).Assembly
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-02-08
        • 1970-01-01
        • 2017-01-08
        • 1970-01-01
        • 2020-04-23
        相关资源
        最近更新 更多