【问题标题】:Console does not contain a definition for ReadKey while using Roslyn使用 Roslyn 时控制台不包含 ReadKey 的定义
【发布时间】:2017-03-13 13:42:03
【问题描述】:

我正在尝试动态编译代码并在运行时执行它。所以我以http://www.tugberkugurlu.com/archive/compiling-c-sharp-code-into-memory-and-executing-it-with-roslyn 为指导。

示例中给出的代码运行良好。但是,如果我使用Console.ReadKey(),它会给我错误CS0117: 'Console' does not contain a definition for 'ReadKey'。我在某处读到这是因为 dotnet core 不支持ReadKey ('Console' does not contain a definition for 'ReadKey' in asp.net 5 console App),但我目前正在使用"Microsoft.NETCore.App",如果我在代码中显式使用它而不是在使用 Roslyn 时使用它,Console.ReadKey() 可以完美运行。

  • 这是 Roslyn 的问题还是我做错了什么?
  • "Microsoft.NETCore.App"dotnet core 是同一个东西吗?我怀疑我可能正在使用其他东西作为我的目标(这允许我使用 ReadKey)和 Roslyn 的 dotnet 核心
  • 是否可以将 Roslyn 的目标更改为其他目标?

提前致谢。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;

namespace DemoCompiler
{
    class Program
    {
        public static void roslynCompile()
        {
            string code = @"
    using System;
    using System.Text;

    namespace RoslynCompileSample
    {
        public class Writer
        {
            public void Write(string message)
            {
                Console.WriteLine(message);
                Console.ReadKey();
            }
        }
    }";
            SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code);

            string assemblyName = Path.GetRandomFileName();
            MetadataReference[] references = new MetadataReference[]
            {
                MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location),
                MetadataReference.CreateFromFile(typeof(Enumerable).GetTypeInfo().Assembly.Location)
            };

            CSharpCompilation compilation = CSharpCompilation.Create(
                assemblyName,
                syntaxTrees: new[] { syntaxTree },
                references: references,
                options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

            using (var ms = new MemoryStream())
            {
                EmitResult result = compilation.Emit(ms);

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

                    foreach (Diagnostic diagnostic in failures)
                        Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
                }
                else
                {
                    ms.Seek(0, SeekOrigin.Begin);
                    Assembly assembly = AssemblyLoadContext.Default.LoadFromStream(ms);
                    var type= assembly.GetType("RoslynCompileSample.Writer");
                    var instance = assembly.CreateInstance("RoslynCompileSample.Writer");
                    var meth = type.GetMember("Write").First() as MethodInfo;
                    meth.Invoke(instance, new [] {assemblyName});
                }
            }

        }
    }
}

编辑:我试图引用System.Console.dll,但我在这和System.Private.CoreLib 之间发生了冲突。我该如何解决?

【问题讨论】:

  • 我不是 .NET 核心方面的专家,但考虑到它围绕​​组件设计的程度,可以很好地猜测 typeof(object)typeof(Enumerable) 不足以引入所有您在主项目中拥有的引用(在 .NET Core 之外,它是 - Console 在 .NET 中位于 mscorlib 中,但不在 .NET Core 中)。特别是,您缺少System.Console.dll,我猜这是罪魁祸首。您需要参考一大堆库来获取默认 C# 项目中的内容:)
  • 阅读您的评论后,我尝试引用System.Console.dll。但是,我在两个程序集之间发生了冲突。我猜这是写在这里的一个已知问题github.com/dotnet/roslyn/issues/16211
  • 我不会认为这是一个“已知问题”,因为您必须等待我们修复它:人们经常使用“typeof(object).Assembly.Location”模式来初始化编译只是从根本上不会在 .NET Core 世界中工作。这是一种在某些 .NET Framework 方案中恰好可以使用的快捷方式。如果您使用 .NET Core,则需要以其他方式收集内容。
  • @JasonMalinowski 推荐的简单“其他方式”是什么?

标签: c# .net-core roslyn


【解决方案1】:

一种方法是将&lt;PreserveCompilationContext&gt;true&lt;/PreserveCompilationContext&gt;* 添加到您的项目文件中,然后使用Microsoft.Extensions.DependencyModel.DependencyContext 获取当前项目的所有参考程序集:

var references = DependencyContext.Default.CompileLibraries
    .SelectMany(l => l.ResolveReferencePaths())
    .Select(l => MetadataReference.CreateFromFile(l));

这似乎避免了您遇到的错误,但会导致以下警告:

警告 CS1701:假设“System.Console”使用的程序集引用“System.Runtime,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a”与标识“System.Runtime,Version=4.1.0.0,Culture=”匹配中立,PublicKeyToken=b03f5f7f11d50a3a' of 'System.Runtime',你可能需要提供运行时策略

As far as I can tell, this should not be an issue.


* 这假设您使用的是 csproj/VS2017。如果您仍在使用 project.json/VS2015,则可以使用 "buildOptions": { "preserveCompilationContext": true } 完成相同的操作。

【讨论】:

  • VSCode 呢?
  • @Nick_McCoy 我相信 VS Code 只使用您安装的任何版本的 .Net Core SDK。因此,如果您有 1.0.0(不是预览版),那么 csproj 说明应该适合您。
【解决方案2】:

https://github.com/dotnet/roslyn/issues/13267 找到了类似的问题,解决方案。

我已经解决了这个问题(据我所知):

从以下位置复制 System.Runtime.dllSystem.Runtime.Extensions.dllC:\Users\&lt;user&gt;\.nuget\packages\System.Runtime\4.1.0\ref\netstandard1.5 并将它们放在我项目的 References 文件夹中。因此,我没有引用mscorlib.dllSystem.Private.CoreLib.dll,而是引用这些

我试过了,发现只引用 System.Runtime.dll 就足够了(至少在我的情况下)。为了清楚起见,我现在使用 MetadataReference.CreateFromFile("System.Runtime.dll") 而不是 MetadataReference.CreateFromFile(typeof(Object).GetTypeInfo().Assembly.Location),它直接引用工作目录中存在的 DLL。

唯一的缺点是我必须将 dll 与我的项目一起分发,但这没关系,因为我现在可以在 .net 核心上正确使用 Roslyn,而不是将基础框架更改为完整的 .net

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-25
    • 2011-04-13
    • 2013-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-01
    • 2015-11-18
    相关资源
    最近更新 更多