假设我有一个像这样的.NET类:
public class Person { public string Name { get; set; } public int Id { get; set; } }
Visual Studio有一个名为Extract Interface的漂亮的重构工具,它将提取一个IPerson
接口并Person
实现它.有没有办法从Visual Studio外部以编程方式执行此操作?如果不能以编程方式完成,我甚至会使用shell脚本.
[编辑]实际上,我最多有50个类,每个类都有相互依赖.
反思会起作用,但它会在整个事物中引入另一个皱纹.这些类已经由生成xsd.exe
.所以,如果我理解正确,我需要采取的步骤是:
从中生成类xsd.exe
.
动态编译类,以便我可以使用反射.
反映它们,发出接口,并编辑原始类以实现所述接口.
一般来说,我赞成放弃接口并直接使用类,但出于各种原因我不能.
我后来遇到了同样的问题,找到了上面接受的解决方案,并决定在这里添加更多信息和细节,关于我如何使用反射和其他功能以编程方式在源代码中生成接口.(如果您不关心使用自定义属性标记源代码,请跳过此第一个代码块)
为了灵活性,我创建了名为GenerateInterface 和GenerateInterfaceMember的自定义属性(只是我自己的创建)来标记我的类和成员我想要显式导出到接口的部分(或者你可以默认导出所有公共成员,无论你喜欢什么).例如,我将这样的属性应用于Store类......
[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...
有关自定义属性的信息,请参阅MSDN.
代码生成的东西......
注意:您无需使用上面显示的自定义属性即可执行以下所有操作.
我使用反射来迭代类型和成员.您只想抓住类中的所有公共成员作为创建接口的候选者.
在System.Type上使用反射获取System.Reflection.PropertyInfo,.MethodInfo等实例后,我创建了一个与语言无关的代码编译单元,由System.CodeDom.CodeCompileUnit类表示,其中MSDN有很好的例子代码 - 在这里看到它..代码编译单元实例是您需要构建的,用于描述您希望以与语言无关的方式生成的源代码.
一旦创建了CodeCompileUnit实例,就可以将它提供给各种FCL方法以满足您的需求,比如生成源代码,在内存或磁盘中发出MSIL代码等.要生成源代码我使用System.CodeDom.Compiler.CodeDomProvider查看MSDN样本除了下面的攻击解决方案...
CodeDomProvider是特定于语言的地方 - 这些语言嵌套在Microsoft命名空间下,例如:
using Microsoft.CSharp; // for C# using Microsoft.VisualBasic; // for VB.NET
有两种方法可以创建CodeDomProvider实例,比如这里的MSDN代码示例显示了一个静态方法'.CreateProvider':
CodeDomProvider provider = null; // snip... case "CSharp": provider = CodeDomProvider.CreateProvider("CSharp"); break; case "Visual Basic": provider = CodeDomProvider.CreateProvider("VisualBasic"); break;
或者,您可以直接实例化提供程序
Microsoft.CSharp.CSharpCodeProvider csProvider = new CSharpCodeProvider(); Microsoft.VisualBasic.VBCodeProvider vbProvider = new VBCodeProvider();
在provider.GenerateCodeFromCompileUnit(..)等提供程序实例上使用方法,并将CodeCompileUnit实例提供给它.此示例代码为StringBuilder实例生成C#源代码.
// 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....
你已经生成了源代码.
在输出sourceCode字符串时,我最终得到了这样的东西.很好的微软预先设置了关于自动生成文件的评论 - 让它感觉更"官方".
//------------------------------------------------------------------------------ //// 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. // //------------------------------------------------------------------------------ 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); } }
我的小免责声明:有很多方法可以生成和发出代码.这里显示的是以编程方式生成源代码的一种方法.有些方法有重载并且需要复杂的参数 - 你必须在MSDN中查找错综复杂的内容.有些方法在1.1,2.0等之间发生了变化.最后,一些完成代码生成的方法已经过时,已经被替代方法所取代; MSDN记录了所有这些事情.
希望这里显示的代码可以作为指导,开始以编程方式生成接口,或生成任何类型的代码.
注意:此示例未显示如何将生成的源代码与现有代码集成,或如何将其发布到MSIL或附加到现有的汇编文件 - 我看到的其他答案显示与这些解决方案相关的示例.
总之:反思.
编写一些带有类对象的代码,反映其公共方法,并编写一个文本文件,这是该类实现的接口的定义,这是非常可行的.
但是,确定WHICH方法属于某个接口(例如,一个类可以实现多个,对吧?)并将正确的符号添加到类的源代码中需要做更多的工作.