2013-06-07 15 views
11

Chcę uzyskać System.Type dali string określająca (pierwotne) RODZAJ za C# przyjazną nazwę zasadniczo drogę kompilator C# robi podczas odczytu kodu źródłowego C#.Convert „C# przyjazny typu” nazwa do rzeczywistego typu: „int” => typeof (int)

Uważam, że najlepszym sposobem na opisanie tego, co robię, jest test jednostkowy.

Mam nadzieję, że istnieje ogólna technika, która może sprawić, że spełnią się wszystkie poniższe twierdzenia, zamiast próbować kodowania specjalnych przypadków dla specjalnych nazw C#.

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[,][,][][,][][])); 
} 

Co próbowałem dotąd:

To pytanie jest uzupełnieniem an older question of mine pytaniem, jak uzyskać „przyjazną nazwę” od typu.

Odpowiedź na to pytanie brzmi: używaj CSharpCodeProvider

using (var provider = new CSharpCodeProvider()) 
{ 
    var typeRef = new CodeTypeReference(typeof(int)); 
    string friendlyName = provider.GetTypeOutput(typeRef); 
} 

nie mogę dowiedzieć się, jak (lub jeśli to możliwe), aby zrobić to w drugą stronę i uzyskać rzeczywisty typ C# od CodeTypeReference (IT posiada również konstruktor, że trwa string)

var typeRef = new CodeTypeReference(typeof(int)); 
+0

_ "Oto unit-test" _ - Więc, chcesz nas napisać implementacja? Pokaż, co próbujesz. – CodeCaster

+7

To był najprostszy i najprostszy sposób opisania, o co mi chodzi. Nie próbujesz być mądrym dupkiem ani czymkolwiek. Myślałem, że inni programiści to docenią. –

+0

Nie o to chodzi. Opisujesz tylko to, czego chcesz, a nie to, czego próbujesz. Jeśli chcesz otrzymać kod, zadzwoń do programisty i zapłać go. Tutaj masz pokazać wysiłek badawczy. @aquaraga Nie pytam o to, o co pyta, ponieważ samo to jest całkiem jasne. – CodeCaster

Odpowiedz

4

Oto sposób to zrobić za pomocą 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 + ")"); 
     } 
    } 
} 
+0

Podoba mi się! Nieco bardziej gadatliwa niż moja implementacja Roslyn, ale bierzesz też pod uwagę kilka wspólnych przestrzeni nazw. +1 –

+1

Btw rzeczywisty "skompilowany fragment kodu" można zapisać jako proste wyrażenie: ... Execute ("typeof (" + type + ")"); nie ma potrzeby stosowania fałszywej klasy. I Execute() ma przeciążoną wersję generyczną, która oszczędza ci późniejszą obsadę. Zobacz moją odpowiedź :) –

+0

Dobra, warto zaktualizować moją odpowiedź na syntaktyczne cukierki :) –

5

pseudonimy jak „int”, „bool” itp nie są częścią .NET Framework. Wewnętrznie są konwertowane na System.Int32, System.Boolean itp. Type.GetType ("int") powinien zwrócić wartość null. Najlepszym sposobem na uzyskanie tego jest posiadanie słownika do odwzorowania aliasu na ich typ, np.

 Dictionary<string, Type> PrimitiveTypes = new Dictionary<string, Type>(); 
     PrimitiveTypes.Add("int", typeof(int)); 
     PrimitiveTypes.Add("long", typeof(long)); 
     etc.etc.. 
+5

Nie ma potrzeby używania 'GetType (" System.Int32 ")' - 'typeof (int)' będzie w porządku –

+0

@MarcGravell Dziękuję Marc .. Będę edytować moją odpowiedź. Pozwoli to również uniknąć wszelkiego błędu w pisaniu. – Yogee

7

Nie masz większości, jeśli już się udało?

Poniżej przedstawiono wszystkie wbudowane typy C# według http://msdn.microsoft.com/en-us/library/ya5y69ds.aspx oraz 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(); 
     } 
    } 
} 

ta opiera się na kilku założeniach, które uważam za prawidłowe w momencie pisania tego tekstu:

  • wszystkie wbudowane w C# typy są częścią pliku mscorlib.dll.
  • Wszystkie wbudowane typy C# są aliasami typów zdefiniowanych w przestrzeni nazw System.
  • Tylko wbudowane nazwy typów C# zwrócone przez połączenie z numerem CSharpCodeProvider.GetTypeOutput nie mają pojedynczego znaku "." w nich.

wyjściowa:

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 

Teraz mam tylko siedzieć i czekać na Eric przyjść i powiedzieć mi, jak źle jestem. Przyjąłem swój los.

+0

+1 Podoba mi się to - bez sztywnych łańcuchów :) Ale co z tablicami typów pierwotnych? Zaktualizowałem pytanie pod kątem kompletności. Powinienem był to określić od początku, przepraszam. –

+0

Przyznam +1 dla ostatniego komentarza, jeśli mógłbym :)) –

3

Oto jeden ze sposobów, aby to zrobić:

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); 
} 

a następnie, jeśli przejdzie w "int", "int []" itd masz odpowiedni typ powrotem.

+0

+1, to działa, możesz również dołączyć odpowiednie instrukcje 'using' (jak' using System.Collections.Generic; ') zanim zdefiniujesz klasa, dzięki czemu możesz przekazać parametry takie jak 'GetType (" Lista ")'. –

+0

+1 Przyjemnie i za pomocą wbudowanych narzędzi. Spójrz też na moją odpowiedź - nie można nie zauważyć, że dla tego prostego kodu (w zasadzie wykonywania 'typeof (coś)') podejścia Roslyn lub Mono jako usługa są bardziej proste. –

+0

Ponadto, po bliższym przyjrzeniu się: dlaczego wykonać 'typeof (type) .FullName', a następnie' Type.GetType (fullName) ', czy nie jest łatwiej po prostu zwrócić' typeof (type) 'w pierwszej kolejności? –

0

Oto moje ukłucie w to. Zbliżyłem się go za pomocą dwóch podobnych biblioteki:

  • Mono C# kompilator-as-a-service
  • MS Roslyn kompilator C#

Myślę, że to bardzo czyste i proste.

Tutaj używam kompilatora-kompilatora C# w Mono, a mianowicie klasy Mono.CSharp.Evaluator. (Jest ona dostępna jako Nuget package nazwane, nic dziwnego, 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 obok: Odpowiednikiem z "the friends in building 41" aka Roslyn ...

Później:

Roslyn jest prawie tak samo łatwe do zainstalowania - kiedy już odkryjesz, co jest co. Skończyło się na tym, że użyłem Nuget package "Roslyn.Compilers.CSharp" (lub otrzymam je jako VS add-in). Po prostu ostrzegam, że Roslyn wymaga projektu .NET 4.5.

Kod jest nawet czystsze:

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; 
    } 
Powiązane problemy