I want to get a System.Type
given a string
that specifies a (primitive) type\'s C# friendly name, basically the way the C# compiler d
Don't you have most of if worked out already?
The following gives you all built-in C# types as per http://msdn.microsoft.com/en-us/library/ya5y69ds.aspx, plus void
.
using Microsoft.CSharp;
using System;
using System.CodeDom;
using System.Reflection;
namespace CSTypeNames
{
class Program
{
static void Main(string[] args)
{
// Resolve reference to mscorlib.
// int is an arbitrarily chosen type in mscorlib
var mscorlib = Assembly.GetAssembly(typeof(int));
using (var provider = new CSharpCodeProvider())
{
foreach (var type in mscorlib.DefinedTypes)
{
if (string.Equals(type.Namespace, "System"))
{
var typeRef = new CodeTypeReference(type);
var csTypeName = provider.GetTypeOutput(typeRef);
// Ignore qualified types.
if (csTypeName.IndexOf('.') == -1)
{
Console.WriteLine(csTypeName + " : " + type.FullName);
}
}
}
}
Console.ReadLine();
}
}
}
This is based on several assumptions which I believe to be correct as at the time of writing:
System
namespace.CSharpCodeProvider.GetTypeOutput
do not have a single '.' in them.Output:
object : System.Object
string : System.String
bool : System.Boolean
byte : System.Byte
char : System.Char
decimal : System.Decimal
double : System.Double
short : System.Int16
int : System.Int32
long : System.Int64
sbyte : System.SByte
float : System.Single
ushort : System.UInt16
uint : System.UInt32
ulong : System.UInt64
void : System.Void
Now I just have to sit and wait for Eric to come and tell me just how wrong I am. I have accepted my fate.
Here's a way to do it by using Roslyn:
using System;
using System.Linq;
using Roslyn.Scripting.CSharp;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(GetType("int[,][,][][,][][]"));
Console.WriteLine(GetType("Activator"));
Console.WriteLine(GetType("List<int[,][,][][,][][]>"));
}
private static Type GetType(string type)
{
var engine = new ScriptEngine();
new[] { "System" }
.ToList().ForEach(r => engine.AddReference(r));
new[] { "System", "System.Collections.Generic" }
.ToList().ForEach(ns => engine.ImportNamespace(ns));
return engine
.CreateSession()
.Execute<Type>("typeof(" + type + ")");
}
}
}
Here's one way to do it:
public Type GetType(string friendlyName)
{
var provider = new CSharpCodeProvider();
var pars = new CompilerParameters
{
GenerateExecutable = false,
GenerateInMemory = true
};
string code = "public class TypeFullNameGetter"
+ "{"
+ " public override string ToString()"
+ " {"
+ " return typeof(" + friendlyName + ").FullName;"
+ " }"
+ "}";
var comp = provider.CompileAssemblyFromSource(pars, new[] { code });
if (comp.Errors.Count > 0)
return null;
object fullNameGetter = comp.CompiledAssembly.CreateInstance("TypeFullNameGetter");
string fullName = fullNameGetter.ToString();
return Type.GetType(fullName);
}
Then if you pass in "int", "int[]" etc you get the corresponding type back.
Here's my stab at it. I approached it using two similar libraries:
I think it's pretty clean and straightforward.
Here I'm using Mono's C# compiler-as-a-service, namely the Mono.CSharp.Evaluator class. (It's available as a Nuget package named, unsurprisingly, Mono.CSharp)
using Mono.CSharp;
public static Type GetFriendlyType(string typeName)
{
//this class could use a default ctor with default sensible settings...
var eval = new Mono.CSharp.Evaluator(new CompilerContext(
new CompilerSettings(),
new ConsoleReportPrinter()));
//MAGIC!
object type = eval.Evaluate(string.Format("typeof({0});", typeName));
return (Type)type;
}
Up next: The counterpart from "the friends in building 41" aka Roslyn...
Later:
Roslyn is almost as easy to install - once you figure out what's what. I ended up using Nuget package "Roslyn.Compilers.CSharp" (Or get it as a VS add-in). Just be warned that Roslyn requires a .NET 4.5 project.
The code is even cleaner:
using Roslyn.Scripting.CSharp;
public static Type GetFriendlyType(string typeName)
{
ScriptEngine engine = new ScriptEngine();
var type = engine.CreateSession()
.Execute<Type>(string.Format("typeof({0})", typeName));
return type;
}
Alias names like 'int','bool' etc. are not part of .NET Framework. Internally they are converted into System.Int32, System.Boolean etc. Type.GetType("int") should return you null. Best way to appoach this is by having a dictionary to map alias with their type e.g.
Dictionary<string, Type> PrimitiveTypes = new Dictionary<string, Type>();
PrimitiveTypes.Add("int", typeof(int));
PrimitiveTypes.Add("long", typeof(long));
etc.etc..