Is there a way to programmatically extract an interface?

Say I have a .NET class:

public class Person { public string Name { get; set; } public int Id { get; set; } } 

Visual Studio has a great refactoring tool called Extract Interface, which extracts the IPerson interface and has Person implements it. Is there a way to do this programmatically from outside of Visual Studio? I would even take a shell script if this cannot be done programmatically.

[EDIT] In fact, I would have up to 50 classes, each of which depended on each other.

Reflection will work, but it will introduce another wrinkle into it all. Classes are already generated using xsd.exe . So, if I understood correctly, the steps that I would need to take would be the following:

  • Generate classes from xsd.exe .
  • Compile classes on the fly so I can use reflection.
  • Flip them, emit the interfaces, and edit the source classes to implement the specified interfaces.

Actually, I would have simply ported the interfaces and used the classes directly, but for various reasons I cannot.

+4
source share
5 answers

In a word: reflection.

It is possible to write some code that takes a class object, reflects its public methods, and writes a text file that defines the interface that the class implements.

However, it is a bit more to decide which WHICH methods belong to the interface (for example, a class can implement more than one, right?) And add the correct notation to the class source code.

+6
source

I came later with the same question, found the decision above and decided to add more information and details here regarding how I used reflection and other functions to create interfaces in the source code programmatically. (Skip this first block of code if you don't want to mark the source with custom attributes to generate the code)

For flexibility, I created custom attributes called GenerateInterface and GenerateInterfaceMember (just my own creations) to mark my classes and members for the parts that I want to explicitly export to interfaces (or you can just export all public users by default, whatever), For example, I apply attributes like this to the Store class ...

 [GenerateInterface(InterfaceName="IStore", NamespaceName="Com.Example.GenerationTest")] class Store : BusinessObject { string _name; [GenerateInterfaceMember] public event EventHandler handler; internal Store(IDomain domain, string name) : base(domain) { _name = name; } [GenerateInterfaceMember] public string GetSomething(int a, int b, Random r, Store s) { throw new NotImplementedException(); } //etc... 

For user attribute information, see MSDN .

Code Generation Material ...

Note. You can do all of the following without using the custom attributes shown above.

I used reflection to repeat types and members. You just want to capture all the public members in your classes as candidates for creating interfaces.

After receiving instances of System.Reflection.PropertyInfo, .MethodInfo, etc. in System.Type using reflection, I then created a code compilation unit, which is an agnostic of the language and is represented by the System.CodeDom.CodeCompileUnit class , of which MSDN has a good code example - see here. . A code compilation block instance is what you need to compile to describe the source code you want to create, regardless of language.

After creating an instance of CodeCompileUnit, you can pass it to various FCL methods to meet your needs, for example, generate source code, emit MSIL code in memory or on disk, etc. To generate the source code, I used System.CodeDom.Compiler.CodeDomProvider See the MSDN sample in addition to the solution hacked together below ...

CodeDomProvider is where things become language specific - these languages ​​are nested in the Microsoft namespace, for example:

 using Microsoft.CSharp; // for C# using Microsoft.VisualBasic; // for VB.NET 

There are several ways to create an instance of CodeDomProvider, for example, the example MSDN code shows the static ".CreateProvider" method:

 CodeDomProvider provider = null; // snip... case "CSharp": provider = CodeDomProvider.CreateProvider("CSharp"); break; case "Visual Basic": provider = CodeDomProvider.CreateProvider("VisualBasic"); break; 

Alternatively, you can directly create a provider instance

 Microsoft.CSharp.CSharpCodeProvider csProvider = new CSharpCodeProvider(); Microsoft.VisualBasic.VBCodeProvider vbProvider = new VBCodeProvider(); 

Use methods in the provider instance, such as provider.GenerateCodeFromCompileUnit (..) and pass the CodeCompileUnit instance to it. This sample code generates C # source code in an instance of StringBuilder.

 // me setting up string capture for my purposes StringBuilder szBuilder = new StringBuilder(); StringWriter writer = new StringWriter(szBuilder); // the key statement - writes c# code to StringBuilder using provider instance and compile unit instance csProvider.GenerateCodeFromCompileUnit(compileUnit, writer, new CodeGeneratorOptions()); // solidifying the generated C# code into one string string sourceCode = szBuilder.ToString(); // can write this string to a file, etc.... 

And there you created the source code.

As a result, I get something similar to the output of the sourceCode string. Good Microsoft adds comments about the auto-generated file - makes you feel more "official".

 //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. // Runtime Version:2.0.50727.4200 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ namespace Com.Example.GenerationTest { using System; using Com.Example.BusinessCore; public interface IStore { string Name { get; set; } event System.EventHandler handler; string GetSomething(int a, int b, System.Random r, Store s); } } 

My little disclaimer. There are many ways to generate and emit code. This shows one way to programmatically generate source code. Some methods have overloads and take complex arguments - you will have to look for subtleties in MSDN. Some methods have changed between 1.1, 2.0, etc. Finally, some methods for performing code generation are outdated and have been replaced by alternative means; MSDN notes all of this.

We hope that the code shown here acts as a guide to begin to move forward, creating software interfaces or generating some code in this regard.

Note. This example does not show how to integrate the generated source code with existing code or how to transfer it to MSIL or add it to existing assembly files. The other answers that I see displayed sample designs related to these solutions.

+8
source

Can you tell us more about why you want to do this?

I could not imagine a situation in which I would need this instead of the usual Reflection products.

Why do you want to emit an interface type and not just use type information from a class?

Here is an example of not-so-fast code that emits an interface with all public properties from a type:

 using System; using System.Linq; using System.Reflection; using System.Reflection.Emit; static class Program { public class Person { public string Name { get; set; } public string Email { get; set; } } static void Main() { var personType = typeof(Person); // create a new assembly and module var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly( new AssemblyName("IPerson"), AssemblyBuilderAccess.RunAndSave); var moduleBuilder = asmBuilder.DefineDynamicModule( "IPerson", "IPerson.dll"); // create a new interface type var typeBuilder = moduleBuilder.DefineType("IPerson", TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.Interface); // build properties foreach (var prop in personType.GetProperties()) { var propBuilder = typeBuilder.DefineProperty( prop.Name, prop.Attributes, prop.PropertyType, Type.EmptyTypes); // build the getters and setters method if a public one was available var getter = prop.GetGetMethod(); var setter = prop.GetSetMethod(); var attr = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Abstract; if (getter != null && getter.IsPublic) propBuilder.SetGetMethod(typeBuilder.DefineMethod( getter.Name, attr, getter.ReturnType, getter.GetParameters().Select(p => p.ParameterType).ToArray() )); if (setter != null && setter.IsPublic) propBuilder.SetSetMethod(typeBuilder.DefineMethod( setter.Name, attr, setter.ReturnType, setter.GetParameters().Select(p => p.ParameterType).ToArray() )); } // complete the type creation typeBuilder.CreateType(); // save the result to a file asmBuilder.Save("IPerson.dll"); } } 

You can load the resulting IPerson.dll into Reflector to see the result.

I think you can build from there to meet your needs.

The proper construction of the entire interface is too complex to discuss in detail.

+1
source

If I understand your question correctly, you want to write a program that, when specifying a class name, displays the source code for an interface whose methods / fields correspond to this class. You can easily write such a program using .NET reflection libraries. In particular, the System.Type class has methods such as GetMethods , which allows you to iterate over all the methods defined in the type and choose which ones to include in your interface.

A more difficult task is to decide which methods to extract. For example, do you want to include methods already defined in other interfaces?

0
source

Perhaps you can take advantage of the fact that the classes generated by xsd.exe are partial ?!

0
source

All Articles