Roslyn c # CSharpCompilation - динамическая компиляция - PullRequest
0 голосов
/ 18 октября 2019

Компилятор Roslyn и динамика -> Я пытаюсь скомпилировать свойство, известное только во время выполнения.
Код работает при компиляции в Visual studio. При компиляции с Roslyn динамические свойства «неизвестны».

В этом примере модульного теста у меня есть MyObject, который наследуется от DynamicObject. Свойства предоставляются с простым словарём KeyValue.

При жестком кодировании MyObject я могу вызвать свойство Hello. Я действительно могу использовать любое свойство во время компиляции. Несуществующие свойства могут привести к ошибке во время выполнения. (ожидаемое поведение)

При использовании MyObject в коде, передаваемом компилятору Roslyn, я не могу использовать какое-либо свойство для своего динамического объекта. Здесь свойство 'Hello' выдает мне ошибку:

CS1061 - 'MyObject' does not contain a definition for 'Hello' and no accessible extension method 'Hello' accepting a first argument of type 'MyObject' could be found (are you missing a using directive or an assembly reference?)

Что мне не хватает?

Пример модульного теста:

using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CSharp.RuntimeBinder;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Testing {
    [TestClass]
    public class FullExampleTest {
        [TestMethod]
        public void HardCoded() {
            var map = new Dictionary<string, string>() {
                { "Hello","Foo"},
                { "World","Bar"}
            };
            dynamic src = new MyObject(map);
            Console.WriteLine(src.Hello);

            Assert.AreEqual("Foo Bar", $"{src.Hello} {src.World}");
        }

        [TestMethod]
        public void CompileAtRuntime() {
            string code = @"
                using System;
                using System.Collections.Generic;
                using System.Diagnostics.Contracts;
                using System.Dynamic;
                using System.IO;
                using System.Linq;
                using System.Reflection;
                using Testing;

                namespace MyNamespace{{
                    public class MyClass{{
                        public static void MyMethod(MyObject src){{
                            Console.WriteLine(src.Hello);
                        }}
                    }}
                }}
           ";

            var ns = Assembly.Load("netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51");

            MetadataReference[] references = new MetadataReference[]
            {
                MetadataReference.CreateFromFile(ns.Location), //netstandard
                MetadataReference.CreateFromFile(typeof(Object).Assembly.Location), //mscorlib
                MetadataReference.CreateFromFile(typeof(DynamicObject).Assembly.Location), //System.Core
                MetadataReference.CreateFromFile(typeof(RuntimeBinderException).Assembly.Location),//Microsoft.CSharp
                MetadataReference.CreateFromFile(typeof(Action).Assembly.Location), //System.Runtime
                MetadataReference.CreateFromFile(typeof(FullExampleTest).Assembly.Location) // this assembly
            };


            var comp = CSharpCompilation.Create(
                assemblyName: Path.GetRandomFileName(),
                syntaxTrees: new[] { CSharpSyntaxTree.ParseText(code) },
                references: references,
                options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
            );

            using (var ms = new MemoryStream()) {
                var result = comp.Emit(ms);
                if (!result.Success) {
                    var failures = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error);

                    foreach (Diagnostic diagnostic in failures) {
                        Console.WriteLine($"{diagnostic.Id} - {diagnostic.GetMessage()}");
                    }
                }
                Assert.IsTrue(result.Success, "Compilation failure..");
            }
        }
    }

    public class MyObject : DynamicObject {
        private IDictionary<string, string> _Map;

        public MyObject(IDictionary<string, string> map) {
            _Map = map;
        }

        public override bool TryGetMember(GetMemberBinder binder, out object result) {
            Contract.Assert(binder != null);

            var ret = _Map.TryGetValue(binder.Name, out string value);
            result = value;
            return ret;
        }

        public override bool TryInvokeMember(InvokeMemberBinder binder,     object[] args, out object result) {
            Contract.Assert(binder != null);

            var ret = _Map.TryGetValue(binder.Name, out string value);
            result = value;
            return ret;
        }

    }
}

1 Ответ

2 голосов
/ 18 октября 2019

Ваш динамически скомпилированный код отличается от статически скомпилированного кода. В вашем динамически скомпилированном коде вы явно объявили src как dynamic. Ваш "жестко запрограммированный" пример пытается трактовать как MyObject. Вы бы получили ту же проблему, если бы ваш жестко запрограммированный тест выглядел следующим образом:

    var src = new MyObject(map);
    Console.WriteLine(src.Hello);

Так что вы можете исправить это, приведя ваш src как dynamic:

public static void MyMethod(MyObject src){
    Console.WriteLine(((dynamic)src).Hello);
}

Или, в первую очередь, объявив его динамическим:

public static void MyMethod(dynamic src){
    Console.WriteLine(src.Hello);
}
...