Creating a script with List - c#

I'm attempting to create a script with Microsoft.CodeAnalysis.CSharp.Scripting.
As soon as I add List<> the code errors. I thought I had all the necessary references and usings included, however it still errors stating The type or namespace name 'List<>' could not be found (are you missing a using directive or an assembly reference?
These are my usings in the code
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Diagnostics;
Below is my example unit test
[TestMethod]
public void RunGenericListTest()
{
var code = #"
List<string> strings = new List<string>();
strings.Add(""test string"");
return output;";
var options = ScriptOptions.Default;
options.WithReferences(typeof(System.Collections.CollectionBase).Assembly);
options.WithReferences(typeof(System.Collections.Generic.List<>).Assembly);
options.WithImports("System.Collections");
options.WithImports("System.Collections.Generic");
var result = CSharpScript.RunAsync(code, options).Result;
Debug.WriteLine(result);
}
This errors on the CSharpScript.RunAsync every time. Can someone enlighten me on what I'm missing?

I think the issue is, WithImports does not mutate options but rather returns a copy
var code = #"
List<string> strings = new List<string>();
strings.Add(""test string"");
return strings;";
var options = ScriptOptions.Default
.WithImports("System.Collections.Generic"); // chaining methods would work better here.
// alternatively reassign the variable:
// options = options.WithImports("System.Collections.Generic");
var result = CSharpScript.RunAsync(code, options).Result;
Debug.WriteLine((result.ReturnValue as List<string>).First());

Related

How can I get the fully qualified namespace from a using directive in Roslyn?

When you hover over a "simplified" using directive in VS2015, it shows you the fully-qualified name. How would I get this information via a Roslyn plugin? Would it be using a DiagnosticAnalyzer? A CodeFixProvider?
Reading through source.roslyn.codeplex.com, there's tons of information there, including how to add a using statement, and also how to simplify type names (including using statements), but I'm unable to figure out how to go in reverse to get the fully-qualified name.
With the semantic model you can retrieve information about the semantics that make up your code (evidently) -- this allows you to get specific information about types and other constructs.
For example:
void Main()
{
var tree = CSharpSyntaxTree.ParseText(#"
using X = System.Text;
using Y = System;
using System.IO;
namespace ConsoleApplication1
{
}"
);
var mscorlib = PortableExecutableReference.CreateFromFile(typeof(object).Assembly.Location);
var compilation = CSharpCompilation.Create("MyCompilation", syntaxTrees: new[] { tree }, references: new[] { mscorlib });
var semanticModel = compilation.GetSemanticModel(tree);
var root = tree.GetRoot();
// Get usings
foreach (var usingDirective in root.DescendantNodes().OfType<UsingDirectiveSyntax>())
{
var symbol = semanticModel.GetSymbolInfo(usingDirective.Name).Symbol;
var name = symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
name.Dump();
}
}
Output:
global::System.Text
global::System
global::System.IO
If you use SymbolDisplayFormat.CSharpErrorMessageFormat instead, you will receive
System.Text
System
System.IO
Your choice what you're interested in but as you can see it works just fine with aliases and without.

How to fix ambiguous reference errors?

I have a web app that allows importing of contacts from Hotmail, Yahoo and GMail. I finally have it almost completed but since I added the importing of GMail, I am getting ambiguous reference errors and I am unsure how to fix them without breaking any code.
Here is a screen shot of the errors:
Try to use unique class names as much as possible. This will be the better solution in the end.
Write the entire namespace when referencing
OAuth.OAuthBase a = new ...;
Google.GData.Client.OAuthBase b = new ...;
Make an using alias for one or both:
using n2 = OAuth;
using Google.GData.Client;
n2.OAuthBase a = new ...; // referenced using namespace
OAuthBase b = new ...; // referenced through existing `using`
you can try something like this..
using GoogleOAuthBase = Google.GData.Client.OAuthBase;
namespace abc
{
public class Program
{
//make sure this Google.GData.Client.OAuthBase is instansiateable
var googleBase = new GoogleOAuthBase();
}
}
you can try entire name space as well.
var googleBase = new Google.GData.Client.OAuthBase();

Read the value of a string in the dll that is created dynamicly

I am trying to create a DLL file in runtime ,as a matter of fact i need to save an encoded data to DLL .My code is like this :
class DllFile
{
public static void CreateDllFile(string source)
{
source = #"using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LicensecodeDll
{
class Check
{
public string returnValue()
{
return " + source + ";" + "}}}";
var provider = new CSharpCodeProvider();
var options = new CompilerParameters
{
OutputAssembly = "test.dll"
};
var results = provider.CompileAssemblyFromSource(options, new[] { source });
}
}
}
every thing works fine and my ddl is created ,but i need to read the value that is saved on the dll file ,i mean i need the returnValue.how can i do that?
Best regards.Any ideas will be appreciated.
You can load the assembly dynamically and use reflection to call that method. The code should be like this.
Assembly a = Assembly.Load("test.dll");
Type myType = a.GetType("LicensecodeDll.Check");
MethodInfo myMethod = myType.GetMethod("returnValue");
object obj = Activator.CreateInstance(myType);
myMethod.Invoke(obj, null);
More detail is on MSDN: How to: Load Assemblies into an Application Domain

Roslyn / Find References - Can't properly load Workspace

I'm trying to write some code to find all method invocations of any given method as I am looking to create an open source UML Sequence Diagramming tool. I'm having trouble, however, getting past the first few lines of code :/
The API appears to have changed drastically and I can't seem to infer proper usage by looking at the code.
When I do:
var workspace = new CustomWorkspace();
string solutionPath = #"C:\Workspace\RoslynTest\RoslynTest.sln";
var solution = workspace.CurrentSolution;
I find that workspace.CurrentSolution has 0 Projects. I figured this would be the equivalent to what was previously Workspace.LoadSolution( string solutionFile ) which would then supposedly contain any Projects in the Solution, but I am not finding any success with this path.
I am terribly confused 0.o
If someone could offer some additional guidance as to how I can use the FindReferences API to identify all invocations of a particular method, it would be very much appreciated!
Alternatively, would I be better off taking a static-analysis approach? I would like to support things like lambdas, iterator methods and async.
====================================================================
Edit -
Here is a full example based on the accepted answer:
using System.Linq;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.FindSymbols;
using System.Diagnostics;
namespace RoslynTest
{
class Program
{
static void Main(string[] args)
{
string solutionPath = #"C:\Workspace\RoslynTest\RoslynTest.sln";
var workspace = MSBuildWorkspace.Create();
var solution = workspace.OpenSolutionAsync(solutionPath).Result;
var project = solution.Projects.Where(p => p.Name == "RoslynTest").First();
var compilation = project.GetCompilationAsync().Result;
var programClass = compilation.GetTypeByMetadataName("RoslynTest.Program");
var barMethod = programClass.GetMembers("Bar").First();
var fooMethod = programClass.GetMembers("Foo").First();
var barResult = SymbolFinder.FindReferencesAsync(barMethod, solution).Result.ToList();
var fooResult = SymbolFinder.FindReferencesAsync(fooMethod, solution).Result.ToList();
Debug.Assert(barResult.First().Locations.Count() == 1);
Debug.Assert(fooResult.First().Locations.Count() == 0);
}
public bool Foo()
{
return "Bar" == Bar();
}
public string Bar()
{
return "Bar";
}
}
}
CustomWorkspace is
A workspace that allows manual addition of projects and documents.
Since you're trying to load a solution, you should use the MSBuildWorkspace, which is
A workspace that can be populated by opening MSBuild solution and project files.
You can create a new MSBuildWorkspace and call OpenSolutionAsync with your solutionPath. For the reference finding part, take a look at the SymbolFinder.
Solutions are an MSBuild concept.
You need to create an MSBuildWorkspace and call OpenSolutionAsync().
string solutionPath = #"C:\Workspace\RoslynTest\RoslynTest.sln";
creates a local variable. It has no influence on your CustomWorkspace object.

What is the point of CodeNamespaceImport when the compiler doesn't need nor use it?

Consider the follow code sample (copy-pastable in LINQPad):
void Main()
{
var ttNamespace = new CodeNamespace("SampleNamespace");
ttNamespace.Imports.AddRange(new[]{"System", "System.Text", "System.Threading"}.Select(x => new CodeNamespaceImport(x)).ToArray());
var newType = new CodeTypeDeclaration("SampleClass")
{
TypeAttributes = TypeAttributes.Public
};
var newField = new CodeMemberField(typeof(List<int>), "_field")
{
Attributes = MemberAttributes.Public
};
newField.InitExpression = new CodeObjectCreateExpression(new CodeTypeReference(typeof(List<int>)), new CodeExpression[] { });
newType.Members.Add(newField);
ttNamespace.Types.Add(newType);
var parameters = new CompilerParameters()
{
GenerateExecutable = false,
OutputAssembly = #"C:\test.dll"
};
var ccUnit = new CodeCompileUnit();
ccUnit.Namespaces.Add(ttNamespace);
var result = CodeDomProvider.CreateProvider("C#").CompileAssemblyFromDom(parameters, ccUnit);
Console.WriteLine (result.Errors);
}
As you can see, I explicitly add namespaces System, System.Text and System.Threading. I also utilize the type List<int> but don't provide a namespace import for System.Collections.Generic.
Logically this should result in an error somewhere down the line. Maybe not at compile time, but definitely at runtime. This is not the case though, the code compiles to this:
We can see that all using statements have already been organized for us: redundant ones are left out and required ones are added. Obviously this is very useful but this begs the question:
What is the point of explicit CodeNamespaceImport statements when the compiler doesn't use it anyway?
I should note that completely omitting the adding of my namespaces doesn't change the output at all: it keeps working as it is.
There is no point, typeof(List<int>) is unambiguous in your program and can generate the full type name of List. It isn't very clear where you got the source listing from, I suspect you used a disassembler that automatically figured out which using directives where most useful.
You can modify the program like this to see what the compiler really sees:
var prov = CodeDomProvider.CreateProvider("C#");
var gen = prov.CreateGenerator();
using (var writer = new System.IO.StreamWriter("c:\\temp\\test.cs")) {
gen.GenerateCodeFromCompileUnit(ccUnit, writer, new CodeGeneratorOptions());
}
var result = prov.CompileAssemblyFromDom(parameters, ccUnit);
Which produces:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.34011
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace SampleNamespace {
using System;
using System.Text;
using System.Threading;
public class SampleClass {
public System.Collections.Generic.List<int> _field = new System.Collections.Generic.List<int>();
}
}
As you can tell, the compiler actually sees the full type name and the using directives are not necessary.
At the IL level, there are no namespace imports, all types are always referenced using their full name.
So, when a decompiler generates C# from IL, it doesn't know what namespace imports did the original code contain. This means that a reasonable decompiler will probably generate code that imports all namespaces that are used in a class (unless that would cause conflicts).

Categories

Resources