2015-06-27 9 views
6

Ich versuche ein paar mögliche Techniken zum Generieren eines dynamischen Proxy einer C# -Schnittstelle zur Laufzeit. Bisher habe ich festgestellt, dass Roslyn mich ohne allzu große Reibereien entschädigt hat, aber ich bin ein wenig im Umgang mit generischen Typen. Insbesondere erhalten Sie die zu analysierenden Typnamen.Verfahren zum Generieren von gültigen Typnamen mit generischen in Roslyn

Meine Basis-Workflow ist:

  • Erstellen Sie das Gerüst für usings, einen Namespace und eine Klasse als CompilationUnitSyntax
  • die Schnittstelle Inspizieren
  • auf der Schnittstelle Für jede Methode proxied bekommen, verwenden Sie die MethodInfo um eine MethodDeclarationSyntax mit SyntaxFactory.MethodDeclaration zu erstellen, mit dem Ziel, meine neue dynamische Klasse

Hier ist ein ex reichlich von dem Thema, das ich rätsel. An dieser Stelle scheint es, dass ich brauche eine Zeichenfolge zu analysieren, um ein TypeSyntax zu erhalten (in diesem Fall für den Rückgabetyp), und der einzige Ort, wo ich es nehmen kann, ist von methodInfo.ReturnType.Name:

var methodDecl = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName(methodInfo.ReturnType.Name), methodInfo.Name);

Das Problem ist, SyntaxFactory.ParseTypeName ist ‚gültig‘ C# Syntax Typdeklarationen erwarten, zum Beispiel List<string>, aber die Namen oder Fullnamen Zugriff auf Eigenschaften sind in der Form:

{Name = "List`1" FullName = 
"System.Collections.Generic.List`1[[UnitTests.SamplePoco, UnitTests, 
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]"} System.Type 
{System.RuntimeType} 

die offensichtlich nicht analysieren, mit den Backticks, Mangel an spitzen Klammern usw.

Gibt es eine bessere Brücke zwischen Reflection-Stilklassen (MethodInfo, Typen) und Roslyn-Syntaxeinheiten? Ich probiere auch eine reine Reflektionssender-Lösung aus, aber ich wollte sehen, ob ich ein Roslyn-basiertes hier bekommen könnte.

+3

Sie können 'GetGenericTypeArguments()' rekursiv erweitern, anstatt die Zeichenfolge zu analysieren. – SLaks

+0

Ich sehe nicht genau, wie dieser Ansatz diese Lücke überbrücken kann, vielleicht verpasse ich etwas. Dieser Pfad scheint nur Type-Objekte zurückgeben zu können, was ich bereits habe. Alle Überladungen der Roslyn-Funktion SyntaxFactory.MethodDeclaration() (was ich gerade anrufe) erwarten eine Instanz von TypeSyntax.Das scheint die grundlegende Hürde zu sein, die es zu überwinden gilt. –

+0

Ja; Sie müssten immer noch Code schreiben, um ihn als C# -Stilnamen zu formatieren. – SLaks

Antwort

2

Um eine TypeSyntax aus einem generischen Typ zu erstellen, kann diese statische Factory-Klasse hilfreich sein. Sie müssen die Liste der generischen Argumente für Typen, die Sie generieren möchten, erhalten, aber die Fabrik, die ich am Ende posten kann (auch verfügbar in this gist) half mir, Instanzen von TypeSyntax relativ schmerzlos zu bekommen.

Beispiel dafür, wie es zu benutzen:

// List<Dictionary<string, List<Type>>> 
TypeSyntaxFactory.GetTypeSyntax(
    "List", 
    TypeSyntaxFactory.GetTypeSyntax(
     "Dictionary", 
     TypeSyntaxFactory.GetTypeSyntax(
      "string" 
     ), 
     TypeSyntaxFactory.GetTypeSyntax(
      "List", 
      "Type" 
     ) 
    ) 
) 

Ich bin mir nicht sicher, was der beste Weg aus der Reflexion mit dem Ausgang zu tun wäre, aber man kann wahrscheinlich nimmt Sie nur den Teil der Typkennung vor das "` "Symbol. In meiner IDE ist dieses Symbol kein gültiges Zeichen für Typnamen, daher sollte davon ausgegangen werden, dass es Teil der Reflektionsausgabe ist.

Schließlich ist hier die Kopie dieser gist

public static class TypeSyntaxFactory 
{ 
    /// <summary> 
    /// Used to generate a type without generic arguments 
    /// </summary> 
    /// <param name="identifier">The name of the type to be generated</param> 
    /// <returns>An instance of TypeSyntax from the Roslyn Model</returns> 
    public static TypeSyntax GetTypeSyntax(string identifier) 
    { 
     return 
      SyntaxFactory.IdentifierName(
       SyntaxFactory.Identifier(identifier) 
      ); 
    } 

    /// <summary> 
    /// Used to generate a type with generic arguments 
    /// </summary> 
    /// <param name="identifier">Name of the Generic Type</param> 
    /// <param name="arguments"> 
    /// Types of the Generic Arguments, which must be basic identifiers 
    /// </param> 
    /// <returns>An instance of TypeSyntax from the Roslyn Model</returns> 
    public static TypeSyntax GetTypeSyntax(string identifier, params string[] arguments) 
    { 
     return GetTypeSyntax(identifier, arguments.Select(GetTypeSyntax).ToArray()); 
    } 

    /// <summary> 
    /// Used to generate a type with generic arguments 
    /// </summary> 
    /// <param name="identifier">Name of the Generic Type</param> 
    /// <param name="arguments"> 
    /// Types of the Generic Arguments, which themselves may be generic types 
    /// </param> 
    /// <returns>An instance of TypeSyntax from the Roslyn Model</returns> 
    public static TypeSyntax GetTypeSyntax(string identifier, params TypeSyntax[] arguments) 
    { 
     return 
      SyntaxFactory.GenericName(
       SyntaxFactory.Identifier(identifier), 
       SyntaxFactory.TypeArgumentList(
        SyntaxFactory.SeparatedList(
         arguments.Select(
          x => 
          { 
           if(x is GenericNameSyntax) 
           { 
            var gen_x = x as GenericNameSyntax; 
            return 
             GetTypeSyntax(
              gen_x.Identifier.ToString(), 
              gen_x.TypeArgumentList.Arguments.ToArray() 
             ); 
           } 
           else 
           { 
            return x; 
           } 
          } 
         ) 
        ) 
       ) 
      ); 
    } 
} 
2

ich diese Erweiterung Methode erstellt das Problem zu beheben.

static class SyntaxExtensions 
{ 
    /// <summary> 
    /// Generates the type syntax. 
    /// </summary> 
    public static TypeSyntax AsTypeSyntax(this Type type) 
    { 
     string name = type.Name.Replace('+', '.'); 

     if (type.IsGenericType) { 
      // Get the C# representation of the generic type minus its type arguments. 
      name = name.Substring(0, name.IndexOf("`")); 

      // Generate the name of the generic type. 
      var genericArgs = type.GetGenericArguments(); 
      return SyntaxFactory.GenericName(SyntaxFactory.Identifier(name), 
       SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(genericArgs.Select(AsTypeSyntax))) 
      ); 
     } else 
      return SyntaxFactory.ParseTypeName(name); 
    } 
} 
Verwandte Themen