Компилятор 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;
}
}
}