Signature of a valid method using reflection

I am trying to create a T4 template that will accept the method definitions in the interface and play the signature and call the base method with the parameters passed. An interface defines a lot of methods, so rewriting them every time an interface change becomes very complex. Another complication is the interface - this is a common interface with possible generic methods and universal parameters. Until now, the only way to find a real signature (without the “1” definition for generics) is to completely rebuild it, which becomes very cumbersome.

In case there is such a signature in my interface:

ICar Drive<TCar>(Expression<Func<TWheel, bool>> wheels, int miles) 

Is there a way to fully reproduce this with reflection without having to extract all the MethodInfo information, or is there a quick way to get the line above so I can write it in my T4?

Any help would be greatly appreciated!

+6
source share
1 answer

When I need to generate code, I often look at the System.CodeDom namespace. It allows you to create a logical representation of the code, and then get the appropriate source code for what you created. However, I don’t know if I can say that this method is not as “cumbersome” as you said in your answer (and this, of course, assumes a “dissection” of MethodInfo . However, this gives you a pretty decent foundation Just by going to the interface that you want to "clone", the name of the new class and base class that you want to extend as follows:

 var code = GenerateCode(typeof(TestInterface<>), "MyNewClass", typeof(TestBaseClass<>)); 

will result in the following:

 //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. // Runtime Version:4.0.30319.237 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ namespace MyNamespace { using System; using System.Linq.Expressions; public class MyNewClass<TWheel> : TestInterface<TWheel>, TestBaseClass<TWheel> { public MyNamespace.ICar Drive<TCar>(Expression<Func<TWheel, bool>> wheels, int miles) { return base.Drive(wheels, miles); } } } 

Alternatively, you can change a few characters in the code and switch to the VB provider, and you will get Visual Basic output (perhaps not useful, but just cool):

 '------------------------------------------------------------------------------ ' <auto-generated> ' This code was generated by a tool. ' Runtime Version:4.0.30319.237 ' ' Changes to this file may cause incorrect behavior and will be lost if ' the code is regenerated. ' </auto-generated> '------------------------------------------------------------------------------ Option Strict Off Option Explicit On Imports System Imports System.Linq.Expressions Namespace MyNamespace Public Class MyNewClass(Of TWheel) Inherits TestInterface(Of TWheel) Implements TestBaseClass(Of TWheel) Public Function Drive(Of TCar)(ByVal wheels As Expression(Of Func(Of TWheel, Boolean)), ByVal miles As Integer) As MyNamespace.ICar Return MyBase.Drive(wheels, miles) End Function End Class End Namespace 

Here is the Beast GenerateCode . Hopefully the comments can explain what happens:

 public static string GenerateCode(Type interfaceType, string generatedClassName, Type baseClass) { //Sanity check if (!interfaceType.IsInterface) throw new ArgumentException("Interface expected"); //I can't think of a good way to handle closed generic types so I just won't support them if (interfaceType.IsGenericType && !interfaceType.IsGenericTypeDefinition) throw new ArgumentException("Closed generic type not expected."); //Build the class var newClass = new CodeTypeDeclaration(generatedClassName) { IsClass = true, TypeAttributes = TypeAttributes.Public, BaseTypes = { //Include the interface and provided class as base classes MakeTypeReference(interfaceType), MakeTypeReference(baseClass) } }; //Add type arguments (if the interface is generic) if (interfaceType.IsGenericType) foreach (var genericArgumentType in interfaceType.GetGenericArguments()) newClass.TypeParameters.Add(genericArgumentType.Name); //Loop through each method foreach (var mi in interfaceType.GetMethods()) { //Create the method var method = new CodeMemberMethod { Attributes = MemberAttributes.Public | MemberAttributes.Final, Name = mi.Name, ReturnType = MakeTypeReference(mi.ReturnType) }; //Add any generic types if (mi.IsGenericMethod) foreach (var genericParameter in mi.GetGenericArguments()) method.TypeParameters.Add(genericParameter.Name); //Add the parameters foreach (var par in mi.GetParameters()) method.Parameters.Add(new CodeParameterDeclarationExpression(MakeTypeReference(par.ParameterType), par.Name)); //Call the same method on the base passing all the parameters var allParameters = mi.GetParameters().Select(p => new CodeArgumentReferenceExpression(p.Name)).ToArray(); var callBase = new CodeMethodInvokeExpression(new CodeBaseReferenceExpression(), mi.Name, allParameters); //If the method is void, we just call base if (mi.ReturnType == typeof(void)) method.Statements.Add(callBase); else //Otherwise, we return the value from the call to base method.Statements.Add(new CodeMethodReturnStatement(callBase)); //Add the method to our class newClass.Members.Add(method); } //TODO: Also add properties if needed? //Make a "CompileUnit" that has a namespace with some 'usings' and then // our new class. var unit = new CodeCompileUnit { Namespaces = { new CodeNamespace(interfaceType.Namespace) { Imports = { new CodeNamespaceImport("System"), new CodeNamespaceImport("System.Linq.Expressions") }, Types = { newClass } } } }; //Use the C# prvider to get a code generator and generate the code //Switch this to VBCodeProvider to generate VB Code var gen = new CSharpCodeProvider().CreateGenerator(); using (var tw = new StringWriter()) { gen.GenerateCodeFromCompileUnit(unit, tw, new CodeGeneratorOptions()); return tw.ToString(); } } /// <summary> /// Helper method for expanding out a type with all it generic types. /// It seems like there should be an easier way to do this but this work. /// </summary> private static CodeTypeReference MakeTypeReference(Type interfaceType) { //If the Type isn't generic, just wrap is directly if (!interfaceType.IsGenericType) return new CodeTypeReference(interfaceType); //Otherwise wrap it but also pass the generic arguments (recursively calling this method // on all the type arguments. return new CodeTypeReference(interfaceType.Name, interfaceType.GetGenericArguments().Select(MakeTypeReference).ToArray()); } 
+8
source

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


All Articles