I'm trying out a few possible techniques for generating a dynamic proxy of a C# interface at runtime. So far I've found Roslyn has taken me a fair away without too much friction, but I'm a bit stuck on dealing with generic types. In particular, getting type names to parse.
My basic workflow is:
CompilationUnitSyntax
MethodInfo
to build a MethodDeclarationSyntax
using SyntaxFactory.MethodDeclaration
, with the goal to my new dynamic classHere's an example of the issue I'm puzzling over. At this point it seems that I need to parse a string to get a TypeSyntax
(in this case for the return type), and the only place I can take it is from methodInfo.ReturnType.Name
:
var methodDecl = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName(methodInfo.ReturnType.Name), methodInfo.Name);
The problem is SyntaxFactory.ParseTypeName
is expecting 'valid' C# syntax type declarations, for example List<string>
, but accessing the Name or FullName properties are in 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 won't parse, with the backticks, lack of angle brackets etc.
Is there a better bridge between Reflection style classes (MethodInfo, Types) and Roslyn syntax units? I'm also trying out a pure reflection emit style solution as well, but wanted to see if I could get a Roslyn based one going here.
I created this extension method to address the issue.
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 );
}
}
In order to create a TypeSyntax from a generic type, then this static factory class might help. You will need to get the list of generic arguments for types you wish to generate, but the factory I post at the bottom (also available in this gist) helped me get instances of TypeSyntax
relatively painlessly.
Example of how to use it:
// List<Dictionary<string, List<Type>>>
TypeSyntaxFactory.GetTypeSyntax(
"List",
TypeSyntaxFactory.GetTypeSyntax(
"Dictionary",
TypeSyntaxFactory.GetTypeSyntax(
"string"
),
TypeSyntaxFactory.GetTypeSyntax(
"List",
"Type"
)
)
)
I'm not sure what the best way would be to deal with the output from reflection, but you could probably just take the substring of the type identifier before the "`" symbol. In my IDE, this symbol isn't a valid character for type names, so it should be safe to assume that it is part of the reflection type output.
Finally, here is the 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;
}
}
)
)
)
);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With