I want to create a Visual Studio Extension(VSIX) using Roslyn. This extension should find all invocations, then looks for their definitions to analyse attributes defined on them. The definition of the method can be any where in the solution.
My first class is like this:
namespace MainProject
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(BusinessProject.Calc.AddNumbers(5, 8));
}
}
}
My second class is something like the following:
namespace BusinessProject
{
public class Calc
{
[CustomAttr("Do something")]
public static long AddNumbers(int a, int b)
{
return a + b;
}
}
}
In above sample, in class Program, I have an invocation of AddNumbers method. I want to 1) analyse the code, 2) find this invocation, 3) get all attributes of the reference method in class Calc, 4) process attributes parameters, 5) and then make a warning/error if needed.
I can analyse current class to find all invocations by RegisterCodeBlockAction, But what I can't do is accessing the entire solution to find definition of the invocation and after that, accessing attributes of the reference.
How can I access the entire solution in RegiserCodeBlockAction?
You're calling the wrong method.
You actually want your analyzer to run on every method invocation, not every code block.
Therefore, you should call RegisterSyntaxNodeAction, and pass SyntaxKind.InvocationExpression.
It will then call you with an InvocationExpressionSyntax which has all the information you need (mostly in the Semantic Model).
As #SLaks mentioned in his comment, by using SemanticModel.Compilation.SyntaxTrees we can access all of the source codes in solution. I found the method and its parameters. But this has a problem which you can't get the TypeInfo of objects using SemanticModel.GetTypeInfo. You have to create a new Semantic Model like as follows:
foreach (var tree in context.SemanticModel.Compilation.SyntaxTrees)
{
var compilation = CSharpCompilation.Create("MyCompilation",
syntaxTrees: new[] { tree }, references: new List<MetadataReference>());
var syntaxRoot = tree.GetRoot();
var model = compilation.GetSemanticModel(tree);
var targetMethod = syntaxRoot.DescendantNodes().OfType<MethodDeclarationSyntax>().FirstOrDefault(f => f.Identifier.ToString() == "Class name to find");
if (targetMethod == null)
continue;
var typeInfo = model.GetTypeInfo(targetMethod.First().ParameterList.Parameters[i].ChildNodes().First());
// Do any thing with typeInfo
}
Related
I understand how I can execute entire scripts using Roslyn in C# but what I now want to accomplish is to compile a class inside the script, instantiate it, parse it to an interface and then invoke methods that the compiled and instantiated class implements.
Does Roslyn expose such functionality? Can you someone please point me to such approach?
Thanks
I think you can do what you want for example like this:
namespace ConsoleApp2 {
class Program {
static void Main(string[] args) {
// create class and return its type from script
// reference current assembly to use interface defined below
var script = CSharpScript.Create(#"
public class Test : ConsoleApp2.IRunnable {
public void Run() {
System.Console.WriteLine(""test"");
}
}
return typeof(Test);
", ScriptOptions.Default.WithReferences(Assembly.GetExecutingAssembly()));
script.Compile();
// run and you get Type object for your fresh type
var testType = (Type) script.RunAsync().Result.ReturnValue;
// create and cast to interface
var runnable = (IRunnable)Activator.CreateInstance(testType);
// use
runnable.Run();
Console.ReadKey();
}
}
public interface IRunnable {
void Run();
}
}
Instead of returning type you created from script you can also use globals and return it that way:
namespace ConsoleApp2 {
class Program {
static void Main(string[] args) {
var script = CSharpScript.Create(#"
public class Test : ConsoleApp2.IRunnable {
public void Run() {
System.Console.WriteLine(""test"");
}
}
MyTypes.Add(typeof(Test).Name, typeof(Test));
", ScriptOptions.Default.WithReferences(Assembly.GetExecutingAssembly()), globalsType: typeof(ScriptGlobals));
script.Compile();
var globals = new ScriptGlobals();
script.RunAsync(globals).Wait();
var runnable = (IRunnable)Activator.CreateInstance(globals.MyTypes["Test"]);
runnable.Run();
Console.ReadKey();
}
}
public class ScriptGlobals {
public Dictionary<string, Type> MyTypes { get; } = new Dictionary<string, Type>();
}
public interface IRunnable {
void Run();
}
}
Edit to answer your comment.
what if I know the name and type of the class in the script? My
understanding is that script.Compile() adds the compiled assembly to
gac? Am I incorrect? If I then simply use
Activator.CreateInstance(typeofClass) would this not solve my problem
without even having to run the script
Compiled assembly is not added to gac - it is compiled and stored in memory, similar to how you can load assembly with Assembly.Load(someByteArray). Anyway, after you call Compile that assembly is loaded in current app domain so you can access your types without RunAsunc(). Problem is this assembly has cryptic name, for example: ℛ*fde34898-86d2-42e9-a786-e3c1e1befa78#1-0. To find it you can for example do this:
script.Compile();
var asmAfterCompile = AppDomain.CurrentDomain.GetAssemblies().Single(c =>
String.IsNullOrWhiteSpace(c.Location) && c.CodeBase.EndsWith("Microsoft.CodeAnalysis.Scripting.dll"));
But note this is not stable, because if you compile multiple scripts in your app domain (or even same script multiple times) - multiple such assemblies are generated, so it is hard to distinguish between them. If that is not a problem for you - you can use this way (but ensure that you properly test all this).
After you found generated assembly - problems are not over. All your script contents are compiled under wrapping class. I see its named "Submission#0" but I cannot guarantee it's always named like that. So suppose you have class Test in your script. It will be child class of that wrapper, so real type name will be "Submission#0+Test". So to get your type from generated assembly it's better to do this:
var testType = asmAfterCompile.GetTypes().Single(c => c.Name == "Test");
I consider this approach somewhat more fragile compared to previous, but if previous are not applicable for you - try this one.
Another alternative suggested in comments:
script.Compile();
var stream = new MemoryStream();
var emitResult = script.GetCompilation().Emit(stream);
if (emitResult.Success) {
var asm = Assembly.Load(stream.ToArray());
}
That way you create assembly yourself and so do not need to search it in current app domain.
This question is not about a method I can mark with [System.Obsolete]. The method I wanna ignore is in a dll I don't have control over.
I use a 3rd party library which contains an extension method for objects. This leads to confusion and may cause problems in the future.
Is there any way to mark this extension method (or all the extension methods from a certain dll ) as obsolete externally or prevent this extension method appearing in intellisense. The problematic method is :
public static class ExtensionMethods
{
public static bool IsNumeric(this object obj)
{
if (obj == null)
return false;
return obj.GetType().IsPrimitive || obj is double || (obj is Decimal || obj is DateTime) || obj is TimeSpan;
}
}
You can do this with a Roslyn Code Analyzer. The following code will create a DiagnosticAnalyzer that will give a compiler warning if String.EndsWith() is used.
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ForbiddenMethodsAnalyzer : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor("Forbidden",
"Don't use this method!",
"Use of the '{0}' method is not allowed",
"Forbidden.Stuff",
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: "This method is forbidden");
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeSyntaxNode, SyntaxKind.InvocationExpression);
}
private static void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context)
{
var invocationExpression = (InvocationExpressionSyntax)context.Node;
var memberAccessExpression = invocationExpression.Expression as MemberAccessExpressionSyntax;
if (memberAccessExpression?.Name.ToString() == "EndsWith")
{
var memberSymbol = context.SemanticModel.GetSymbolInfo(memberAccessExpression).Symbol as IMethodSymbol;
var containingType = memberSymbol.ContainingType;
if (containingType.ContainingNamespace.Name == "System" && containingType.Name == "String")
{
var diagnostic = Diagnostic.Create(Rule, invocationExpression.GetLocation(), memberAccessExpression.ToString());
context.ReportDiagnostic(diagnostic);
}
}
}
}
There are 3 options to use an Analyzer like this:
Add the DiagnosticAnalyzer code directly to your project. It will apply only to that solution.
Create a
class library with the DiagnosticAnalyzer in it, and distribute it
as a Nuget package. It will apply only to solutions that use the package.
Compile a full VSIX extension containing the
class. The analyzer will work on any solution you load.
This is the first project I've done that uses the Roslyn Code Analysis functionality, so unfortunately I don't understand everything that is going on here. I started with the default Analyzer template and tried various methods, stepped through code, and looked at variables with the watch windows until I found the information I needed for this functionality.
The basic process is to register a SyntaxNode Analysis function, filtered to expressions that invoke a method. Within that method I check to see if the Name of the MemberAccessExpressionSyntax being examined is "EndsWith". If it is, I get the ContainingType that the method is on, and check to see if it is on the String class in the System namespace. If it is, I create a Diagnostic instance from a DiagnosticDescriptor to tell the IDE where the problem is, and how much of a problem it represents (A warning in this case, I could make it a full Error if I wanted, which would prevent the code from compiling). It is also possible to present the user with different options to automatically fix the error, but I haven't explored that yet.
A lot of the information came from this tutorial, as well as a whole lot of trial and error.
The best way to handle this would be to use Roslyn and create your own code analyzer, or use an existing tool like FxCop.
However, I found a very non-elegant workaround for this.
In your project you can create a class with the same name as the referenced class, residing in an identical namespace, with the exact same method. Now mark your method as obsolete.
The below code sample has a reference to a library with an ExtensionMethods class which is defined in the External namespace. In the line marked with (*) comment, where the method is called using the static method call syntax, the compiler warns you that the type ExtensionMethods conflicts with an imported type. It also tells you that the method is obsolete (since you have shadowed the imported type, it sees your definition). So when you invoke the method, your code will run. In the line marked with (**) comment, where the method is called using the extension method call syntax, the compiler says that the call is ambiguous and the code won't compile. The only workaround I know of is to turn this call into line (*), which will produce the obsolete warning.
With this solution you will be able to call other extension methods from the referenced type if you use the extension method syntax, provided you don't have the same method defined in your class.
using System;
using External;
namespace Internal
{
class Program
{
static void Main(string[] args)
{
ExtensionMethods.IsNumeric(new object()); // (*)
new object().IsNumeric(); // (**)
}
}
}
namespace External
{
public static class ExtensionMethods
{
[Obsolete]
public static bool IsNumeric(this object o)
{
if (obj == null)
return false;
return obj.GetType().IsPrimitive || obj is double || (obj is Decimal || obj is DateTime) || obj is TimeSpan;
}
}
}
I need to create attribute for functions that will create files according to a given name before the call to the function or even if there is no call to the function.
For example, if I have function with atribute [some("C:\\hello.txt")]:
[some("C:\\hello.txt")]
private void foo()
{
// do something
}
When I will run the application it will create this file ("C:\hello.txt") before calling the function or even if there is no call to the function..
I tried with two techniques:
1. Creating the file in the constructor
2. Creating the file with reflection.
But none of them worked for me.
First try (with constructor):
I tried create the file in the constructor every time there is a new attribute.
In this method I tried to create the file before the enter to the Main function.
While it parsing the functions it will find the attributes and create the files.
Expected:
Two files should be created:
1. C:\hello.txt
2. C:\bye.txt
In reality => nothing happen.
[some("C:\\hello.txt")]
private void foo()
{
// do something
}
[some("C:\\bye.txt")]
private void foo()
{
// do something
}
public class someAttribute : Attribute
{
public someAttribute(string fileToCreate)
{
// File.Create(fileToCreate);
Console.WriteLine("Create file " + fileToCreate);
}
}
static void Main(string[] args)
{
// something
}
Second try (with reflection):
Expected:
One file should be created:
1. C:\hello.txt
In reality => "types" variables is empty and nothing is being created.
[some(fileToCreate = "C:\\hello.txt")]
private void foo()
{
// do something
}
public class someAttribute : Attribute
{
public string fileToCreate {get; set;}
}
static void Main(string[] args)
{
var types = from t in Assembly.GetExecutingAssembly().GetTypes()
where t.GetCustomAttributes<someAttribute>().Count() > 0
select t;
foreach(var t in types) // types is null
{
string n = t.Name;
foreach(var p in t.GetProperties())
{
// File.Create(fileToCreate)
Console.WriteLine(p.fileToCreate);
}
}
}
Your first attempt didn't work because an attribute's constructor is run when an attribute is examined See When is a custom attribute's constructor run? for additional details. It won't be run just by the fact that a method has that attribute in the code. So reflection will be needed to get a list of methods that have the desired attribute.
Your second attempt came close, but didn't work because you only looked at the attributes attached to the class types. You'll need to go one step further to look at the methods within the classes.
I came up with a solution, but be warned that it could affect performance since it looks at every type and method in the assemblies linked to your project. You may want to limit the assemblies to only the ones that you can expect to have someAttribute. See C#: List All Classes in Assembly for some examples on how to do this.
static void Main()
{
var methods = AppDomain.CurrentDomain.GetAssemblies()
//Get a sequence of all types in the referenced assemblies
.SelectMany(assembly => assembly.GetTypes())
//Get a sequence of all the methods in those types.
//The BindingFlags are needed to make sure both public and non-public instance methods are included.
//Otherwise private methods are skipped.
.SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
//And finally, filter to only those methods that have someAttribute attached to them
.Where(method => Attribute.IsDefined(method, typeof(someAttribute)));
foreach (MethodInfo methodInfo in methods)
{
IEnumerable<someAttribute> SomeAttributes = methodInfo.GetCustomAttributes<someAttribute>();
foreach (var attr in SomeAttributes)
{
//Here, you can create the file.
Console.WriteLine(attr.fileToCreate);
}
}
}
Forgive me for just grasping these terms, I'm on the edge of my C# knowledge here and need to ask for guidance.
I have a DLL which includes two classes and a form (additional class) one of the classes workitems has public (string name, int id).
// in the DLL:
public class workitems {
public string name {get;set;}
public int id{get;set;}
}
The workhorse class has a variable used in several functions
// in the DLL:
public class workhorse {
List<workitems> WorkLoad = new List<workitems>();
public function DoThings() { ..... stuff ...... }
}
In another program, I need to call this dll (I assume via reflection). I've tried
// in a separate C# script that needs to call this via reflection
Assembly asm = Assembly.LoadFile(thedll);
but I can't figure out how to load workitems into the variable, and then call a function from the dll with those workitems... I'm getting confused with type/class/methodinfo/.GetType... any guidance would be appreciated.
From the program that has to call the dll file I need do something like :
otherdll.workload.add( stuff )
otherdll.DoThings(); (which uses the workload from that class)
That code assumes, that you have already your assembly and that Workload is a Field, not property:
//Get workhorse TypeInfo
var type = asm.ExportedTypes.Single(t => t.Name == "workhorse");
// Create instance of workhorse
var obj = Activator.CreateInstance(type);
// Get FieldInfo WorkLoad
var prop = type.GetField("WorkLoad");
// Get object workhorse.WorkLoad
var list = prop.GetValue(obj);
// Get MethodInfo for Add method
var method = prop.FieldType.GetMethod("Add");
// Call it with new object
method.Invoke(list, new [] { (object)new workitems()});
// Get DoThings methodinfo
var doThings = type.GetMethod("DoThings");
// call it without parameters
doThings.Invoke(obj, new object[0]);
I have a list of class names and methods that can only be read during runtime. Is it possible to create a class dynamically like that? I'm currently using C# 4.0.
It is a little unclear whether you want to define a type at runtime and define methods against it, or whether you want to create an instance of an already-written type, and call methods on it.
Fortunately both are possible.
The second scenario is more likely, so you'd want to look at reflection (below) - but note that there are performance penalties associate with this (and small things like "what arguments does the method take" become very important).
For the first scenario, you'd need to look at TypeBuilder, but that is much more complex. Another option would be CSharpCodeProvider and dynamic assembly loading, but again - not trivial by any stretch.
using System;
namespace MyNamespace {
public class Foo {
public void Bar() {
Console.WriteLine("Foo.Bar called");
}
}
}
class Program {
static void Main() {
string className = "MyNamespace.Foo, MyAssemblyName",
methodName = "Bar";
Type type = Type.GetType(className);
object obj = Activator.CreateInstance(type);
type.GetMethod(methodName).Invoke(obj, null);
}
}
To include parameters (comments) you pass an object[] instead of the null:
using System;
namespace MyNamespace {
public class Foo {
public void Bar(string value) {
Console.WriteLine("Foo.Bar called: " + value);
}
}
}
class Program {
static void Main() {
string className = "MyNamespace.Foo, MyAssemblyName",
methodName = "Bar";
Type type = Type.GetType(className);
object obj = Activator.CreateInstance(type);
object[] args = { "hello, world" };
type.GetMethod(methodName).Invoke(obj, args);
}
}
If you are doing this lots (for the same method) there is a way to improve performance via a typed delegate, but this doesn't gain you much for occasional calls.
You can use the IL emit functionality straight out of the .Net framework to accomplish this. You will need to learn IL in order to dynamically generate type information at runtime--this is not for the feint of heart.
Introduction to Creating Dynamic Types with System.Reflection.Emit
As I can't really answer that you are asking I will answer a number of question you might want to ask.
Can I create an instance of class and call its method where both class and method are specified at run time?
Sure. The simple way first. Use a if statements:
var className = "MyClass";
var methodName = "MyMethod";
if (className == typeof(MyClass).Name) {
var instance = new MyClass();
if (methodName == "MyMethod")
instance.MyMethod();
if (methodName == "MyOtherMethod")
instance.MyOtherMethod();
}
Alternatively, you can use Activator.CreateInstance to create an instance of a class for you.
var className = "MyClass";
var methodName = "MyMethod";
//Get a reference to the Assembly that has the desired class. Assume that all classes that we dynamically invoke are in the same assembly as MyClass.
var assembly = typeof(MyClass).Assembly;
//Find the type that we want to create
var type = assembly.GetTypes().FirstOrDefault(t=>t.Name == className);
if(type != null) {
//Create an instance of this type.
var instance = Activator.CreateInstance(type);
//Find the method and call it.
instance.GetType().GetMethod(methodName).Invoke(instance);
}
Can I generate a class at run time?
Yes you can but it's hard. If you are an experienced C# programmer, know a bit of C++ and assembly, you should be able to grock it. If not, don't bother.
Microsoft provides a library to emit Intermediate Language code called, surpose, IL.Emit. There are very few problems that warrant using this method, amongst those are mock object generation and some aspects of Dependency Injection. It is quite likely that your problem is better solved in another fashion.