On first stage I am adding annotations to syntax nodes and replace nodes with new generated nodes.
On second stage when I am analysing modified document (same syntax tree with added annotations) but references in SymbolInfo still refers to unmodified syntax nodes (without annotations).
Is it possible to update or reparse solution or project and update SymbolInfo after adding annotations?
Create simple solution with one C# file:
class ะก
{
void g()
{ }
void f()
{
g();
}
}
And try to parse it with program:
using System.Collections.Generic;
using Roslyn.Compilers;
using Roslyn.Compilers.Common;
using Roslyn.Compilers.CSharp;
using Roslyn.Services;
namespace RoslynExample2
{
class Program
{
static void Main(string[] args)
{
var workspace = Workspace.LoadSolution(#"..\..\..\..\RoslynExampleTest\RoslynExampleTest.sln");
var solution = workspace.CurrentSolution;
foreach (var project in solution.Projects)
{
Annotator annotator = new Annotator();
foreach (var document in project.Documents)
{
CompilationUnitSyntax compilationUnit = (CompilationUnitSyntax)document.GetSyntaxRoot();
var mcu = annotator.AddAnnotations(compilationUnit);
document.UpdateSyntaxRoot(mcu);
}
}
foreach (var project in solution.Projects)
{
foreach (var document in project.Documents)
{
var compilationUnit = document.GetSyntaxRoot();
var semanticModel = document.GetSemanticModel();
MySyntaxWalker sw = new MySyntaxWalker(semanticModel);
sw.Visit((SyntaxNode)compilationUnit);
}
}
}
}
internal class Annotator
{
internal struct SyntaxNodeTuple
{
internal SyntaxNode Origin;
internal SyntaxNode Modified;
internal SyntaxNodeTuple(SyntaxNode origin, SyntaxNode modified)
{
Origin = origin;
Modified = modified;
}
}
private SyntaxNodeTuple AddAnnotation(SyntaxNode s)
{
SyntaxNodeTuple t;
switch (s.Kind)
{
case SyntaxKind.ClassDeclaration:
t = AddAnnotations((ClassDeclarationSyntax)s);
break;
case SyntaxKind.MethodDeclaration:
t = AddAnnotations((MethodDeclarationSyntax)s);
break;
default:
t = new SyntaxNodeTuple();
break;
}
return t;
}
private static T ReplaceNodes<T>(T d, List<SyntaxNodeTuple> tuples)
where T : SyntaxNode
{
T d2 = d;
foreach (var t in tuples)
{
d2 = d2.ReplaceNode(t.Origin, t.Modified);
}
return d2;
}
private void AddAnnotationsToList(SyntaxList<MemberDeclarationSyntax> list, List<SyntaxNodeTuple> tuples)
{
foreach (var m in list)
{
tuples.Add(AddAnnotation(m));
}
}
internal CompilationUnitSyntax AddAnnotations(CompilationUnitSyntax d)
{
List<SyntaxNodeTuple> tuples = new List<SyntaxNodeTuple>();
AddAnnotationsToList(d.Members, tuples);
var d2 = ReplaceNodes(d, tuples);
return d2;
}
internal SyntaxNodeTuple AddAnnotations(ClassDeclarationSyntax d)
{
List<SyntaxNodeTuple> tuples = new List<SyntaxNodeTuple>();
AddAnnotationsToList(d.Members, tuples);
var d2 = ReplaceNodes(d, tuples);
d2 = d2.WithAdditionalAnnotations(new MyAnnotation());
return new SyntaxNodeTuple(d, d2);
}
internal SyntaxNodeTuple AddAnnotations(MethodDeclarationSyntax d)
{
var d2 = d.WithAdditionalAnnotations(new MyAnnotation());
bool hasAnnotation = d2.HasAnnotations(typeof(MyAnnotation)); // annotation exists
return new SyntaxNodeTuple(d, d2);
}
}
class MyAnnotation : SyntaxAnnotation
{ }
partial class MySyntaxWalker : SyntaxWalker
{
private ISemanticModel _semanticModel;
public MySyntaxWalker(ISemanticModel semanticModel)
{
_semanticModel = semanticModel;
}
public override void VisitInvocationExpression(InvocationExpressionSyntax decl)
{
var si = _semanticModel.GetSymbolInfo(decl);
var dsns = si.Symbol.DeclaringSyntaxNodes;
var dsn0 = dsns[0];
bool hasAnnotation = dsn0.HasAnnotations(typeof(MyAnnotation)); // annotation doesn't exists
}
}
}
updated variant:
using System;
using System.Diagnostics;
using Roslyn.Compilers;
using Roslyn.Compilers.Common;
using Roslyn.Compilers.CSharp;
using Roslyn.Services;
namespace RoslynExample2
{
class Program
{
static void Main(string[] args)
{
var workspace = Workspace.LoadSolution(#"..\..\..\..\RoslynExampleTest\RoslynExampleTest.sln");
var solution = workspace.CurrentSolution;
foreach (var projectId in solution.ProjectIds)
{
var project = solution.GetProject(projectId);
foreach (var documentId in project.DocumentIds)
{
var document = project.GetDocument(documentId);
CompilationUnitSyntax compilationUnit = (CompilationUnitSyntax)document.GetSyntaxRoot();
Debug.WriteLine(String.Format("compilationUnit={0} before", compilationUnit.GetHashCode()));
Debug.WriteLine(String.Format("project={0} before", project.GetHashCode()));
Debug.WriteLine(String.Format("solution={0} before", solution.GetHashCode()));
var mcu = new AnnotatorSyntaxRewritter().Visit(compilationUnit);
var project2 = document.UpdateSyntaxRoot(mcu).Project;
if (mcu != compilationUnit)
{
solution = project2.Solution;
}
Debug.WriteLine(String.Format("compilationUnit={0} after", mcu.GetHashCode()));
Debug.WriteLine(String.Format("project={0} after", project2.GetHashCode()));
Debug.WriteLine(String.Format("solution={0} after", solution.GetHashCode()));
}
}
foreach (var projectId in solution.ProjectIds)
{
var project = solution.GetProject(projectId);
foreach (var documentId in project.DocumentIds)
{
var document = project.GetDocument(documentId);
var compilationUnit = document.GetSyntaxRoot();
var semanticModel = document.GetSemanticModel();
Debug.WriteLine(String.Format("compilationUnit={0} stage", compilationUnit.GetHashCode()));
Debug.WriteLine(String.Format("project={0} stage", project.GetHashCode()));
Debug.WriteLine(String.Format("solution={0}", solution.GetHashCode()));
MySyntaxWalker sw = new MySyntaxWalker(semanticModel);
sw.Visit((SyntaxNode)compilationUnit);
}
}
}
}
class AnnotatorSyntaxRewritter : SyntaxRewriter
{
public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
{
node = node.WithAdditionalAnnotations(new MyAnnotation());
return base.VisitMethodDeclaration(node);
}
}
class MyAnnotation : SyntaxAnnotation
{ }
partial class MySyntaxWalker : SyntaxWalker
{
private ISemanticModel _semanticModel;
public MySyntaxWalker(ISemanticModel semanticModel)
{
_semanticModel = semanticModel;
}
public override void VisitMethodDeclaration(MethodDeclarationSyntax decl)
{
bool hasAnnotation = decl.HasAnnotations(typeof(MyAnnotation));
Debug.Assert(hasAnnotation);
}
}
}
The problem in your example is that the variable solution is an immutable object which refers to the solution when it is first loaded. In your code where you call document.UpdateSyntaxRoot(mcu), that actually creates and returns a new IDocument which is in a new IProject, which is in a new ISolution.
Try changing that bit of code to:
Annotator annotator = new Annotator();
foreach (var projectId in solution.ProjectIds)
{
foreach (var documentId in solution.GetProject(projectId).DocumentIds)
{
var document = solution.GetProject(projectId).GetDocument(documentId);
CompilationUnitSyntax compilationUnit = (CompilationUnitSyntax)document.GetSyntaxRoot();
var mcu = annotator.AddAnnotations(compilationUnit);
solution = document.UpdateSyntaxRoot(mcu).Project.Solution;
}
}
Related
I am trying to make a source generator for mapping columns from the google bigquery api client to class properties. I'm having trouble getting custom column names from a ColumnAttribute on the properties. ConstructorArguments is always empty and columnAttribute.AttributeClass in this sample is always an ErrorTypeSymbol. If I try to load that type using compilation.GetTypeByMetadataName("System.ComponentModel.DataAnnotations.Schema.ColumnAttribute") the result is always null.
using System.Collections.Immutable;
using System.Diagnostics;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
namespace BigQueryMapping;
[Generator]
public class BigQueryMapperGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// add marker attribute
context.RegisterPostInitializationOutput(ctx =>
ctx.AddSource("BigQueryMappedAttribute.g.cs", SourceText.From(Attribute, Encoding.UTF8)));
// add static interface
context.RegisterPostInitializationOutput(ctx =>
ctx.AddSource("BigQueryMappedInterface.g.cs", SourceText.From(Interface, Encoding.UTF8)));
// get classes
IncrementalValuesProvider<ClassDeclarationSyntax> classDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: static (s, _) => s is ClassDeclarationSyntax c && c.AttributeLists.Any(),
transform: static (ctx, _) => GetSemanticTargetForGeneration(ctx)
)
.Where(static m => m is not null)!;
IncrementalValueProvider<(Compilation Compilation, ImmutableArray<ClassDeclarationSyntax>Syntaxes)>
compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect());
context.RegisterSourceOutput(compilationAndClasses,
static (spc, source) => Execute(source.Compilation, source.Syntaxes, spc));
static ClassDeclarationSyntax? GetSemanticTargetForGeneration(GeneratorSyntaxContext context)
{
var classDeclarationSyntax = (ClassDeclarationSyntax)context.Node;
foreach (var attributeListSyntax in classDeclarationSyntax.AttributeLists)
{
foreach (var attributeSyntax in attributeListSyntax.Attributes)
{
var fullName = context.SemanticModel.GetTypeInfo(attributeSyntax).Type?.ToDisplayString();
if (fullName == "BigQueryMapping.BigQueryMappedAttribute")
return classDeclarationSyntax;
}
}
return null;
}
static void Execute(Compilation compilation, ImmutableArray<ClassDeclarationSyntax> classes,
SourceProductionContext context)
{
try
{
if (classes.IsDefaultOrEmpty)
return;
var distinctClasses = classes.Distinct();
var classesToGenerate = GetTypesToGenerate(compilation, distinctClasses, context.CancellationToken);
foreach (var classToGenerate in classesToGenerate)
{
var result = GeneratePartialClass(classToGenerate);
context.AddSource($"{classToGenerate.RowClass.Name}.g.cs", SourceText.From(result, Encoding.UTF8));
}
}
catch (Exception e)
{
var descriptor = new DiagnosticDescriptor(id: "BQD001",
title: "Error creating bigquery mapper",
messageFormat: "{0} {1}",
category: "BigQueryMapperGenerator",
DiagnosticSeverity.Error,
isEnabledByDefault: true);
context.ReportDiagnostic(Diagnostic.Create(descriptor, null, e.Message, e.StackTrace));
}
}
}
static IEnumerable<ClassToGenerate> GetTypesToGenerate(Compilation compilation,
IEnumerable<ClassDeclarationSyntax> classes,
CancellationToken ct)
{
var columnAttributeSymbol =
compilation.GetTypeByMetadataName("System.ComponentModel.DataAnnotations.Schema.ColumnAttribute");
foreach (var #class in classes)
{
Debug.WriteLine($"Checking class {#class}");
ct.ThrowIfCancellationRequested();
var semanticModel = compilation.GetSemanticModel(#class.SyntaxTree);
if (semanticModel.GetDeclaredSymbol(#class) is not INamedTypeSymbol classSymbol)
continue;
var info = new ClassToGenerate(classSymbol, new());
foreach (var member in classSymbol.GetMembers())
{
if (member is IPropertySymbol propertySymbol)
{
if (propertySymbol.DeclaredAccessibility == Accessibility.Public)
{
if (propertySymbol.SetMethod is not null)
{
var columnName = propertySymbol.Name;
var columnAttribute = propertySymbol.GetAttributes().FirstOrDefault(a =>
a.AttributeClass!.ToDisplayString() == "Column");
if (columnAttribute is not null)
{
if (!columnAttribute.ConstructorArguments.IsDefaultOrEmpty)
{
var nameArg = columnAttribute.ConstructorArguments.First();
if (nameArg.Value is string name)
{
columnName = name;
}
}
}
info.Properties.Add((columnName, propertySymbol));
}
}
}
}
yield return info;
}
}
static string GeneratePartialClass(ClassToGenerate c)
{
var sb = new StringBuilder();
sb.Append($#"// <auto-generated/>
namespace {c.RowClass.ContainingNamespace.ToDisplayString()}
{{
public partial class {c.RowClass.Name} : BigQueryMapping.IBigQueryGenerated<{c.RowClass.Name}>
{{
public static {c.RowClass.Name} FromBigQueryRow(Google.Cloud.BigQuery.V2.BigQueryRow row)
{{
return new {c.RowClass.Name}
{{");
foreach (var (columnName, property) in c.Properties)
{
// would like to check if key exists but don't see any sort of ContainsKey implemented on BigQueryRow
var tempName = $"___{property.Name}";
var basePropertyType = property.Type.WithNullableAnnotation(NullableAnnotation.None).ToDisplayString();
if (basePropertyType.EndsWith("?"))
{
basePropertyType = basePropertyType.Substring(default, basePropertyType.Length - 1);
}
sb.Append($#"
{property.Name} = row[""{columnName}""] is {basePropertyType} {tempName} ? {tempName} : default,");
}
sb.Append($#"
}};
}}
}}
}}");
return sb.ToString();
}
private record struct ClassToGenerate(INamedTypeSymbol RowClass,
List<(string ColumnName, IPropertySymbol Property)> Properties);
public const string Attribute = /* lang=csharp */ #"// <auto-generated/>
namespace BigQueryMapping {
[System.AttributeUsage(System.AttributeTargets.Class)]
public class BigQueryMappedAttribute : System.Attribute
{
}
}";
public const string Interface = /* lang=csharp */ #"// <auto-generated/>
namespace BigQueryMapping {
public interface IBigQueryGenerated<TRow> {
static TRow FromBigQueryRow(Google.Cloud.BigQuery.V2.BigQueryRow row) => throw new System.NotImplementedException();
}
}";
}
I have tried this with both System.ComponentModel.DataAnnotations.Schema.ColumnAttribute and a custom attribute injected via context.RegisterPostInitializationOutput to similar results. I have also tried rewriting this to use ISourceGenerator instead of IIncrementalGenerator and gotten the same behavior. Am wondering what I need to do to get columnAttribute loading correctly.
Thanks for any help in advance
It's hard to psychic debug the code but this does jump out:
var columnAttribute = propertySymbol.GetAttributes().FirstOrDefault(a =>
a.AttributeClass!.ToDisplayString() == "Column");
I'd guess the class here would be the fully qualified name. You already fetched the ColumnAttribute type earlier, so what'd be even better is to compare the AttributeClass to that type rather than doing string checks like this.
As a semi-related comment, if you're looking for types/members that are annotated with a specific attribute, rather than doing it yourself we have SyntaxValueProvider.ForAttributeWithMetadataName which is pretty heavily optimized to reduce the performance impact on your Visual Studio. It requires 17.3 or higher, but as long as you're OK with that it'll generally help performance.
I am fairly new to C# and working on a project in which I need to build a prefix tree (trie). Searching in the trie should return a list of words matching a given search prefix.
That's the code I have so far, but the search doesnt actually return the value I'm looking for and instead returns "Trees.PrefixTree+d__5". What am I doing wrong or what do I have to change to get it run?
Thank you very much in advance!
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
namespace Trees
{
public class Program
{
public static void Main()
{
//String[] file = File.ReadAllLines(#"C:\Users\samue\Desktop\Neuer Ordner (2)\liste.txt");
string[] dictionary = new string[] { "test", "try", "angle", "the", "code", "is", "isnt" };
PrefixTree trie = new PrefixTree();
var sw = new Stopwatch();
foreach (var word in dictionary)
{
trie.Add(word);
}
//Thread workerThread = new Thread(trie.Search(suchwort);
Console.WriteLine(trie.Search("te"));
}
}
public class PrefixTree
{
private PrefixTreeNode root;
public PrefixTree()
{
root = new PrefixTreeNode(String.Empty);
}
public void Add(string word)
{
AddRecursive(root, word, String.Empty);
}
private void AddRecursive(PrefixTreeNode node, string remainingString, string currentString)
{
if (remainingString.Length <= 0)
{
return;
}
char prefix = remainingString[0];
string substring = remainingString.Substring(1);
if (!node.SubNodes.ContainsKey(prefix))
{
node.SubNodes.Add(prefix, new PrefixTreeNode(currentString + prefix));
}
if (substring.Length == 0)
{
node.SubNodes[prefix].IsWord = true;
return;
}
else
{
AddRecursive(node.SubNodes[prefix], substring, currentString + prefix);
}
}
public IEnumerable<string> Search(string searchString)
{
PrefixTreeNode node = root;
foreach (var search in searchString)
{
if (!node.SubNodes.ContainsKey(search))
{
return new string[0];
}
node = node.SubNodes[search];
}
return FindAllWordsRecursive(node);
}
private IEnumerable<string> FindAllWordsRecursive(PrefixTreeNode node)
{
if (node.IsWord)
{
yield return node.Word;
}
foreach (var subnode in node.SubNodes)
{
foreach (var result in FindAllWordsRecursive(subnode.Value))
{
yield return result;
}
}
}
protected class PrefixTreeNode
{
private readonly Dictionary<char, PrefixTreeNode> subNodes;
private bool isWord;
private readonly string word;
public PrefixTreeNode(string word)
{
subNodes = new Dictionary<char, PrefixTreeNode>();
isWord = false;
this.word = word;
}
public Dictionary<char, PrefixTreeNode> SubNodes { get { return subNodes; } }
public bool IsWord { get { return isWord; } set { isWord = value; } }
public string Word { get { return word; } }
}
}
}
Try changing your Console.WriteLine() to this:
Console.WriteLine(string.Join(", ", trie.Search("te")));
You were not concatenating all the strings together!
I have a failing testcase that depends on an external module.
I want to use Rhino Mock to generate a report on called functions.
I created a minimal example that illustrates my problem:
using NUnit.Framework;
using Rhino.Mocks;
using System;
namespace StackOverflow_namespace
{
public interface IUsefulService
{
object HiddenAmongManyCalls();
}
public class ThirdPartyBase
{
private int a = 42;
public ThirdPartyBase(IUsefulService service)
{
object liveFastDieYoung = service.HiddenAmongManyCalls();
liveFastDieYoung.Equals(a);
}
}
public class MyParty : ThirdPartyBase
{
public MyParty(IUsefulService service) : base(service)
{
}
}
[TestFixture]
class StackOverflow
{
[Test]
public void Hypothetical()
{
IUsefulService service = MockRepository.GenerateMock<IUsefulService>();
try
{
var party = new MyParty(service);
}
catch(Exception e)
{
string[] calls = MagicallyGetTheCallsThatWereMadeToTheMock();
foreach(var call in calls)
{
//with my visual studio testrunner for nunit 3 I can investigate stored console output
Console.WriteLine(call);
}
Assert.Fail("Excpexted no exception but was '" + e.GetType().Name + "': " + e.Message);
}
}
private string[] MagicallyGetTheCallsThatWereMadeToTheMock()
{
return new[]
{
"This is where I am lost, I do not know how to get the calls from the repository."
};
}
}
}
I tried to find something online without success.
Do Rhino Mocks record all calls and can I access that list?
Edit:
An attempt to verify Expectations did not work since I am looking for calls I did not expect.
I could build a list of calls using GetArgumentsForCallsMadeOn. I can reflect on the Interface. I started on a method for that but I currently fail to see how I can convert a MethodInfo to an Action<T>.
private IEnumerable<string> GetCallsList<Interface>(Interface rhinomock)
{
Type interfaceType = typeof(Interface);
List<MethodInfo> interfaceMethodInfos = new List<MethodInfo>();
List<string> returnInfos = new List<string>();
StringBuilder callbuilder = new StringBuilder();
foreach (var property in interfaceType.GetProperties())
{
interfaceMethodInfos.Add(property.GetGetMethod());
interfaceMethodInfos.Add(property.GetSetMethod());
}
foreach (var method in interfaceType.GetMethods())
{
interfaceMethodInfos.Add(method);
}
foreach (var methodinfo in interfaceMethodInfos)
{
Action<Interface> magic = null; //convert methodinfo into action - still missing
var calls = rhinomock.GetArgumentsForCallsMadeOn(magic); //magic is currently null, here be crash
foreach (var call in calls)
{
bool more = false;
callbuilder.Clear().Append(interfaceType.Name).Append('.').Append(methodinfo.Name).Append('(');
foreach (var parameter in call)
{
if (more){ callbuilder.Append(", "); }
if (null == parameter) { callbuilder.Append("<null>"); }
else { callbuilder.Append(parameter.ToString()); }
more = true;
}
callbuilder.Append(')');
string callInfo = callbuilder.ToString();
returnInfos.Add(callInfo);
}
}
return returnInfos;
}
I was able to use reflection to get the output I wanted.
Here is the minimal example where the test fails and the output contains all method calls.
using NUnit.Framework;
using Rhino.Mocks;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
namespace StackOverflow_namespace
{
public interface IUsefulService
{
object HiddenAmongManyCalls();
string TestCall2(string arg1, int arg2);
string FULLACCESS { get; set; }
string READONLY { get; }
}
public class ThirdPartyBase
{
private int a = 42;
public ThirdPartyBase(IUsefulService service)
{
service.TestCall2("callA", 1);
service.TestCall2("callB", 1);
object liveFastDieYoung = service.HiddenAmongManyCalls();
service.TestCall2("callA", 2);
service.TestCall2("callB", 2);
var a = service.FULLACCESS;
var b = service.READONLY;
service.FULLACCESS = "some";
liveFastDieYoung.Equals(a);
}
}
public class MyParty : ThirdPartyBase
{
public MyParty(IUsefulService service) : base(service)
{
}
}
[TestFixture]
class StackOverflow
{
[Test]
public void Hypothetical()
{
IUsefulService service = MockRepository.GenerateMock<IUsefulService>();
try
{
var party = new MyParty(service);
}
catch (Exception e)
{
var calls = GetCallsList(service);
foreach (var call in calls)
{
//with my visual studio testrunner for nunit 3 I can investigate stored console output
Console.WriteLine(call);
}
Assert.Fail("Excpexted no exception but was '" + e.GetType().Name + "': " + e.Message);
}
}
private IEnumerable<string> GetCallsList<Interface>(Interface rhinomock)
{
Type interfaceType = typeof(Interface);
List<MethodInfo> interfaceMethodInfos = new List<MethodInfo>();
List<string> returnInfos = new List<string>();
StringBuilder callbuilder = new StringBuilder();
foreach (var property in interfaceType.GetProperties())
{
AddMethodInfoIfValid(interfaceMethodInfos, property.GetGetMethod());
AddMethodInfoIfValid(interfaceMethodInfos, property.GetSetMethod());
}
foreach (var method in interfaceType.GetMethods())
{
AddMethodInfoIfValid(interfaceMethodInfos, method);
}
foreach (var methodinfo in interfaceMethodInfos)
{
int paramcount = methodinfo.GetParameters().Length;
object[] args = new object[paramcount];
Action<Interface> lambdacall = (i) => methodinfo.Invoke(i, args);
var calls = rhinomock.GetArgumentsForCallsMadeOn(lambdacall);
foreach (var call in calls)
{
bool more = false;
callbuilder.Clear().Append(interfaceType.Name).Append('.').Append(methodinfo.Name).Append('(');
foreach (var parameter in call)
{
if (more) { callbuilder.Append(", "); }
if (null == parameter) { callbuilder.Append("<null>"); }
else {
callbuilder
.Append('(').Append(parameter.GetType().Name).Append(")'")
.Append(parameter.ToString()).Append("'");
}
more = true;
}
callbuilder.Append(')');
string callInfo = callbuilder.ToString();
returnInfos.Add(callInfo);
}
}
return returnInfos;
}
private static void AddMethodInfoIfValid(List<MethodInfo> interfaceMethodInfos, MethodInfo methodinfo)
{
if (null != methodinfo)
{
interfaceMethodInfos.Add(methodinfo);
}
}
}
}
i try to use CompileAssemblyFromSource to change 1 value at my main class.
But when i compile i get error "Could not load file or assembly or one of its dependencies" and this only happens when i try change static value of other class. But if i return some output or wrote anything at Console from this FooClass than all work's fine. But how can i change value of other class?
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
namespace stringToCode
{
class Program
{
public static int q = 0;
static void Main(string[] args)
{
string source = "namespace stringToCode { public class FooClass { public void Execute() { Program.q = 1; } } }";
Console.WriteLine("q=" + q);
using (var foo = new CSharpCodeProvider())
{
var parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
try
{
string location = assembly.Location;
if (!String.IsNullOrEmpty(location))
{
parameters.ReferencedAssemblies.Add(location);
}
}
catch (NotSupportedException)
{}
}
var res = foo.CompileAssemblyFromSource(parameters ,source);
var type = res.CompiledAssembly.GetType("FooClass"); //<- here i has error
var obj = Activator.CreateInstance(type);
var output = type.GetMethod("Execute").Invoke(obj, new object[] { });
Console.WriteLine("q=" + q);
Console.ReadLine();
}
}
}
}
You can't find the type because you have compilation error in your code.You can't access the classes in your current code in this manner. You should at least reference the current assembly in your in-memory assembly.
UPDATE
You have two issues in your code. First, you have to make the class Program public. Then you should specify the full name of type in GetType method.
This code works fine:
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
namespace stringToCode
{
public class Program
{
public static int q = 0;
static void Main(string[] args)
{
string source = "namespace stringToCode { public class FooClass { public void Execute() { Program.q = 1; } } }";
Console.WriteLine("q=" + q);
using (var foo = new CSharpCodeProvider())
{
var parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
try
{
string location = assembly.Location;
if (!String.IsNullOrEmpty(location))
{
parameters.ReferencedAssemblies.Add(location);
}
}
catch (NotSupportedException)
{}
}
var res = foo.CompileAssemblyFromSource(parameters ,source);
var type = res.CompiledAssembly.GetType("stringToCode.FooClass"); //<- here i has error
var obj = Activator.CreateInstance(type);
var output = type.GetMethod("Execute").Invoke(obj, new object[] { });
Console.WriteLine("q=" + q);
Console.ReadLine();
}
}
}
}
How can I get all the classes from a Roslyn compilation?
var sln = Path.Combine(path, "xxx.sln");
var workspace = MSBuildWorkspace.Create();
var solution = await workspace.OpenSolutionAsync(sln);
Project project = solution.Projects.First(x => x.Name == "bbb");
var compilation = await project.GetCompilationAsync();
This is how I visit all the classes in my solution.
class ClassVirtualizationVisitor : CSharpSyntaxRewriter
{
List<string> classes = new List<String>();
public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
{
node = (ClassDeclarationSyntax) base.VisitClassDeclaration(node);
string className = node.Identifier.ValueText;
classes.Add(className); // save your visited classes
return node;
}
}
Now use the visited classes:
var classVisitor = new ClassVirtualizationVisitor();
classVisitor.Visit(semanticModel.SyntaxTree.GetRoot());
var classes = classVisitor.classes; // list of classes in your solution
This now is my working Code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.MSBuild;
using System.Threading.Tasks;
namespace Kardex.LC3xx.CreateApiDokumentation
{
//Patch to work with VS2013
// https://support.microsoft.com/en-us/kb/2971005
class Program
{
private static void Main(string[] args)
{
Run(args).Wait();
Console.ReadLine();
}
private async static Task Run(string[] args)
{
var path = Path.GetDirectoryName(typeof (Program).Assembly.Location);
var sln = Path.Combine(path, "xxx.sln");
var workspace = MSBuildWorkspace.Create();
var solution = await workspace.OpenSolutionAsync(sln);
Project project = solution.Projects.First(x => x.Name == "bbbb");
var compilation = await project.GetCompilationAsync();
foreach (var #class in compilation.GlobalNamespace.GetNamespaceMembers().SelectMany(x=>x.GetMembers()))
{
Console.WriteLine(#class.Name);
Console.WriteLine(#class.ContainingNamespace.Name);
}
var classVisitor = new ClassVirtualizationVisitor();
foreach (var syntaxTree in compilation.SyntaxTrees)
{
classVisitor.Visit(syntaxTree.GetRoot());
}
var classes = classVisitor.Classes;
}
class ClassVirtualizationVisitor : CSharpSyntaxRewriter
{
public ClassVirtualizationVisitor()
{
Classes = new List<ClassDeclarationSyntax>();
}
public List<ClassDeclarationSyntax> Classes { get; set; }
public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
{
node = (ClassDeclarationSyntax)base.VisitClassDeclaration(node);
Classes.Add(node); // save your visited classes
return node;
}
}
}
}
Create a SymbolVisitor that overrides VisitNamedType to process each type (which may not be a class).
Then, pass it to compilation.Assembly.Accept().