Methodology for creating valid generic type names in Roslyn

I am trying to use several possible methods to create a dynamic C # interface proxy at runtime. Until now, I have found that Roslyn took my detachment without much friction, but I was a bit stuck in communicating with the generic types. In particular, getting type names for parsing.

My main workflow:

  • Create scaffolding to use, namespace and class like CompilationUnitSyntax
  • Verify that the interface is proxied.
  • For each method on the interface, use MethodInfo to build MethodDeclarationSyntax using SyntaxFactory.MethodDeclaration to create a new dynamic class

Here is an example of a problem that I am puzzling. At the moment it seems that I need to parse the string to get TypeSyntax (in this case for the return type), and the only place I can take is from methodInfo.ReturnType.Name :

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

Problem SyntaxFactory.ParseTypeName expects "valid" declarations of C # syntax type, such as List<string> , but access to the Name or FullName properties takes the 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} 

which, obviously, will not be parsed, with reverse windows, no angle brackets, etc.

Is there a better bridge between Reflection style classes (MethodInfo, Types) and Roslyn syntactic units? I am also trying to find a solution with a clean reflection, but would like to see if I can get Roslin coming here.

+6
source share
2 answers

To create TypeSyntax from a generic type, this static factory class can help. You will need to get a list of general arguments for the types you want to create, but the factory I placed below (also available in this gist ) helped me get TypeSyntax instances relatively painlessly.

Usage example:

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

I'm not sure the best way is to process the result from the reflection, but you could just take the substring of the type identifier before the ``. In my IDE, this character is not a valid character for type names, so you should safely assume that it is part of the reflection type output.

Finally, here is a copy of this 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
source

I created this extension method to solve the problem.

 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 ); } } 
+2
source

Source: https://habr.com/ru/post/989859/


All Articles