KA1
KA1

Reputation: 643

How to get declared variables and other definitions

I'm using Roslyn scripting API in embedded REPL, from time to time I need to know what variables has been defined in current session, also what to all other definitions (classes,interfaces...) and what references and imported namespace.

I couldn't find anything in API can give me that kind of information,I know it's there somewhere.

Dose Scripting API use some unusual implementation, like in-memory assembly updated every time? if so how to get access to that assembly.

Upvotes: 4

Views: 408

Answers (1)

vossad01
vossad01

Reputation: 11948

I do not know if there is a Roslyn specific API, but it looks like you can get at the information using Reflection. It is a bit of a chore. I am using the REPL in Visual Studio to approximate your situation.

using System.Reflection;

Assembly.GetExecutingAssembly().DefinedTypes

Shows there is a type for every iteration of the loop plus some additional for classes defined. Luckily for you the Submission types are numbered with latter submissions having a higher number i.e. Submission#11 comes after Submission#5.

Variables defined in the REPL appear as field in the Session type that corresponds to the execution of the loop in which it was defined. The call to see all defined variables in a type would be as follows:

chosenType.GetFields(BindingFlags.Instance | BindingFlags.Public)

The Roslyn REPL lets you re-declare variables, masking the previously declared one from new callers. Now consider the "session" as a stack of all the submissions (the oldest being on bottom). If you traverse down the stack, the first occurrence of a given field name would be the active variable with that name in the REPL. The field info retrieved provides the type in addition to the name.

Here is an example session (including output) showing how you can see all variables declared within the session.

> using System.Reflection;
> var a = 1; 
> var b = "c";
> var c = from type in Assembly.GetExecutingAssembly().DefinedTypes.Reverse()
      from variable in type.GetFields(BindingFlags.Instance | BindingFlags.Public)
      select variable;
> foreach (var info in c ) { 
     if (info.FieldType != typeof(Roslyn.Services.InteractiveHostObject)) {
         Console.WriteLine(info);
     }
  }
System.Collections.Generic.IEnumerable`1[System.Reflection.FieldInfo] c
System.String b
Int32 a

If the same name appears twice, it is the first one listed is the one available in the session. The above takes advantage of the fact that the types return happen to be ordered oldest to youngest. You will probably want to sort it yourself to be sure.

For whatever reason (bug maybe?) I have to use if statements within the foreach loop, using a LINQ where clause does not give expected results.

There are many more reflection methods that may be useful depending on what all you want to find, such as GetMethods and GetEvents. If you want everything then there is a GetMembers See Type Methods.

I hope someone can provide an easier way.

Upvotes: 6

Related Questions