Преобразовать имя "С# дружественного типа" в фактический тип: "int" => typeof (int)
Я хочу получить System.Type
с учетом string
, который задает дружественное имя типа (примитивного) типа С#, в основном, как это делает компилятор С# при чтении исходного кода на С#.
Я считаю, что лучший способ описать, что мне нужно, - это unit тест.
Моя надежда заключается в том, что существует общий метод, который может сделать все приведенные ниже утверждения, а не пытаться жестко программировать специальные случаи для специальных имен С#.
Type GetFriendlyType(string typeName){ ...??... }
void Test(){
// using fluent assertions
GetFriendlyType( "bool" ).Should().Be( typeof(bool) );
GetFriendlyType( "int" ).Should().Be( typeof(int) );
// ok, technically not a primitive type... (rolls eyes)
GetFriendlyType( "string" ).Should().Be( typeof(string) );
// fine, I give up!
// I want all C# type-aliases to work, not just for primitives
GetFriendlyType( "void" ).Should().Be( typeof(void) );
GetFriendlyType( "decimal" ).Should().Be( typeof(decimal) );
//Bonus points: get type of fully-specified CLR types
GetFriendlyName( "System.Activator" ).Should().Be(typeof(System.Activator));
//Hi, Eric Lippert!
// Not Eric? https://stackoverflow.com/a/4369889/11545
GetFriendlyName( "int[]" ).Should().Be( typeof(int[]) );
GetFriendlyName( "int[,]" ).Should().Be( typeof(int[,]) );
//beating a dead horse
GetFriendlyName( "int[,][,][][,][][]" ).Should().Be( typeof(int[,][,][][,][][]) );
}
То, что я пробовал до сих пор:
Этот вопрос является дополнением к старому вопросу о моей проблеме, в котором спрашивается, как получить "дружественное имя" от типа.
Ответ на этот вопрос: use CSharpCodeProvider
using (var provider = new CSharpCodeProvider())
{
var typeRef = new CodeTypeReference(typeof(int));
string friendlyName = provider.GetTypeOutput(typeRef);
}
Я не могу понять, как (или, если возможно) сделать это наоборот и получить фактический тип С# из CodeTypeReference
(он также имеет ctor, который принимает string
)
var typeRef = new CodeTypeReference(typeof(int));
Ответы
Ответ 1
Здесь можно сделать это с помощью 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 + ")");
}
}
}
Ответ 2
Разве у вас больше всего не получилось?
Ниже приведены все встроенные типы С# по http://msdn.microsoft.com/en-us/library/ya5y69ds.aspx, плюс 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();
}
}
}
Это основано на нескольких предположениях, которые, как я считаю, являются правильными на момент написания:
- Все встроенные типы С# являются частью mscorlib.dll.
- Все встроенные типы С# - это псевдонимы типов, определенных в пространстве имен
System
.
- Только имена встроенных типов С#, возвращаемые вызовом
CSharpCodeProvider.GetTypeOutput
, не имеют ни одного символа '.' в них.
Вывод:
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
Теперь мне просто нужно сидеть и ждать, пока Эрик придет и скажет мне, насколько я ошибаюсь. Я принял свою судьбу.
Ответ 3
Такие псевдонимы, как "int", "bool" и т.д., не являются частью .NET Framework. Внутри они преобразуются в System.Int32, System.Boolean и т.д. Type.GetType( "int" ) должен вернуть вам значение null. Лучший способ уловить это, имея словарь для сопоставления псевдонимов с их типом, например.
Dictionary<string, Type> PrimitiveTypes = new Dictionary<string, Type>();
PrimitiveTypes.Add("int", typeof(int));
PrimitiveTypes.Add("long", typeof(long));
etc.etc..
Ответ 4
Вот один из способов сделать это:
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);
}
Затем, если вы передадите "int", "int []" и т.д., вы получите соответствующий тип.
Ответ 5
Здесь мой удар по нему. Я подошел к нему, используя две аналогичные библиотеки:
- Компилятор Mono С# как услуга
- MS Roslyn С# компилятор
Я думаю, что это довольно чисто и просто.
Здесь я использую компилятор Mono С# как-a-service, а именно класс Mono.CSharp.Evaluator
. (Доступно как пакет Nuget, что неудивительно, 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;
}
Далее: копия "друзья в здании 41 "aka Roslyn...
Позже:
Roslyn почти так же просто установить - как только вы выясните, что. Я закончил использование пакет Nuget "Roslyn.Compilers.CSharp" (или получить его как надстройка VS). Просто предупредите, что Roslyn требует проект .NET 4.5.
Код еще чище:
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;
}