利用反射生成接口列表
程序员文章站
2022-12-15 19:55:37
公司产品对外公布的接口, 评审后才能发布, 然后要求生成接口文档(去除注释, 这样更能检查接口命名是否合理). 之前用的是微软的一个免费工具, 但好久都没有更新了, 对新一点的C#语法不支持, 生成的文档是错误的, 如果不想手动从代码复制方法签名, 只能手写一个工具了 这个段代码以满足公司要求来编写 ......
公司产品对外公布的接口, 评审后才能发布, 然后要求生成接口文档(去除注释, 这样更能检查接口命名是否合理).
之前用的是微软的一个免费工具, 但好久都没有更新了, 对新一点的c#语法不支持, 生成的文档是错误的, 如果不想手动从代码复制方法签名, 只能手写一个工具了
这个段代码以满足公司要求来编写, 没有多余精力去优化代码了, 其中用到了扩展方法导致很多地方不合理. 但是总归不用手动写文档了, 避免了很多无意义的工作.
// first: install-package npoi using npoi.xwpf.usermodel; using system; using system.collections.generic; using system.io; using system.linq; using system.reflection; public static class interfaceexporthelper { static bindingflags bindingflags = bindingflags.public | bindingflags.nonpublic | bindingflags.instance | bindingflags.declaredonly | bindingflags.static; public static int spaceincreasecountofeachlevel = 4; public static string csharpclasscolor = "2b93af"; public static string csharpkeywordcolor = "0000ff"; public static bool isshowparameterdefaultvalue = false; public static void separateinterfacetodocument(string assemblypath, string outputdocpath) { var assembly = assembly.loadfrom(assemblypath); var types = assembly.gettypes().orderby(t => t.name); xwpfdocument doc = new xwpfdocument(); var spacecount = 0; foreach (var type in types) { if (type.isclass) { var p = dealclass(type, doc, spacecount); if (p != null) { p.appendcarriagereturn(); } } else if (type.isenum) { var p = dealenum(type, doc, spacecount); if (p != null) { p.appendcarriagereturn(); } } else if (type.isvaluetype) { var p = dealstruct(type, doc, spacecount); if (p != null) { p.appendcarriagereturn(); } } } var fs = new filestream(outputdocpath, filemode.create); doc.write(fs); fs.close(); } static xwpfparagraph dealclass(type type, xwpfdocument doc, int spacecount) { if (!type.ispublic) { return null; } //if (type.isnestedprivate) //{ // return null; //} if (type.name.startswith("<")) { return null; } var p = doc.createparagraph(); p.appendspaces(spacecount); if (type.ispublic) { p.appendkeywordcsharp("public"); } else { p.appendkeywordcsharp("internal"); } p.appendspaces(); if (type.isabstract && type.issealed) { p.appendkeywordcsharp("static"); p.appendspaces(); } else if (type.issealed) { p.appendkeywordcsharp("sealed"); p.appendspaces(); } else if (type.isabstract) { p.appendkeywordcsharp("abstract"); p.appendspaces(); } p.appendkeywordcsharp("class"); p.appendspaces(); p.appendclasscsharp(type.name); bool hasbasetype = false; if (type.basetype != null && type.basetype != typeof(object)) { hasbasetype = true; p.appendtext(" : "); dispalytype(type.basetype, p); } bool isfisrtinterface = true; foreach (var interfacetype in type.getinterfaces()) { if (!hasbasetype) { p.appendtext(" : "); } if (!isfisrtinterface || hasbasetype) { p.appendtext(", "); } dispalytype(interfacetype, p); isfisrtinterface = false; } p.appendcarriagereturn(); var tempspacecount = spacecount;// - spaceincreasecountofeachlevel > 0 ? spacecount - spaceincreasecountofeachlevel : 0; p.appendspaces(tempspacecount); p.appendtext("{"); p.appendcarriagereturn(); bool hasaddedsomething = false; foreach (var filedinfo in type.getfields(bindingflags)) { if (!filedinfo.ispublic && !filedinfo.isfamily) { continue; } dealfieldinfo(filedinfo, p, spacecount + spaceincreasecountofeachlevel); hasaddedsomething = true; } if (hasaddedsomething) p.appendcarriagereturn(); hasaddedsomething = false; foreach (constructorinfo constructorinfo in type.getconstructors(bindingflags.nonpublic | bindingflags.public | bindingflags.instance)) { if (dealconstructor(constructorinfo, p, spacecount + spaceincreasecountofeachlevel) == true) { hasaddedsomething = true; } } if (hasaddedsomething) p.appendcarriagereturn(); hasaddedsomething = false; foreach (var propertyinfo in type.getproperties(bindingflags)) { if (dealproperty(propertyinfo, p, spacecount + spaceincreasecountofeachlevel) == true) { hasaddedsomething = true; } } if (hasaddedsomething) p.appendcarriagereturn(); hasaddedsomething = false; foreach (methodinfo method in type.getmethods(bindingflags)) { if (dealmethod(method, p, spacecount + spaceincreasecountofeachlevel) == true) { hasaddedsomething = true; } } if (hasaddedsomething) p.appendcarriagereturn(); p.removerun(p.runs.count - 1); p.appendspaces(tempspacecount); p.appendtext("}"); return p; } static bool dealfieldinfo(fieldinfo fieldinfo, xwpfparagraph p, int spacecount) { if (!(fieldinfo.ispublic || fieldinfo.isfamily)) { return false; } //if (fieldinfo.isprivate) //{ // return false; //} p.appendspaces(spacecount); var eventfieldlist = new list<fieldinfo>(); if (fieldinfo.ispublic) { p.appendkeywordcsharp("public"); } else if (fieldinfo.isfamily) { p.appendkeywordcsharp("protected"); } else if (fieldinfo.isassembly) { p.appendkeywordcsharp("internal"); } else if (fieldinfo.isfamilyorassembly) { p.appendkeywordcsharp("internal protected"); } p.appendspaces(); if (fieldinfo.isinitonly) { p.appendkeywordcsharp("readonly"); p.appendspaces(); } if (fieldinfo.isstatic) { p.appendkeywordcsharp("static"); p.appendspaces(); } dispalytype(fieldinfo.fieldtype, p); p.appendspaces(); p.appendtext(fieldinfo.name); p.appendtext(";"); p.appendcarriagereturn(); return true; } static bool dealproperty(propertyinfo propertyinfo, xwpfparagraph p, int spacecount) { accessormodifier? getteraccessormodifier = null, setteraccessormodifier = null; if (propertyinfo.canread) { getteraccessormodifier = getaccessormodifier(propertyinfo.getmethod); } if (propertyinfo.canwrite) { setteraccessormodifier = getaccessormodifier(propertyinfo.setmethod); } var mainaccessormodifier = gethighaccessormodifier(getteraccessormodifier, setteraccessormodifier); if (!(mainaccessormodifier == accessormodifier.public || mainaccessormodifier == accessormodifier.protected)) { return false; } p.appendspaces(spacecount); p.appendkeywordcsharp(accessormodifiertostring(mainaccessormodifier)); p.appendspaces(); dispalytype(propertyinfo.propertytype, p); p.appendspaces(); p.appendtext(propertyinfo.name); p.appendspaces(); p.appendtext("{"); if (propertyinfo.canread && getteraccessormodifier >= accessormodifier.protected) { p.appendspaces(); if (getteraccessormodifier != mainaccessormodifier) { p.appendkeywordcsharp(accessormodifiertostring(getteraccessormodifier.value)); p.appendspaces(); } p.appendkeywordcsharp("get;"); } if (propertyinfo.canwrite && setteraccessormodifier >= accessormodifier.protected) { p.appendspaces(); if (setteraccessormodifier != mainaccessormodifier) { p.appendkeywordcsharp(accessormodifiertostring(setteraccessormodifier.value)); p.appendspaces(); } p.appendkeywordcsharp("set;"); } p.appendspaces(); p.appendtext("}"); p.appendcarriagereturn(); return true; } static bool dealconstructor(constructorinfo constructorinfo, xwpfparagraph p, int spacecount) { if (!(constructorinfo.ispublic || constructorinfo.isfamily)) { return false; } //if (constructorinfo.isprivate) //{ // return false; //} p.appendspaces(spacecount); if (constructorinfo.ispublic) { p.appendkeywordcsharp("public"); } else if (constructorinfo.isfamily) { p.appendkeywordcsharp("protected"); } else if (constructorinfo.isassembly) { p.appendkeywordcsharp("internal"); } else if (constructorinfo.isfamilyorassembly) { p.appendkeywordcsharp("internal protected"); } p.appendspaces(); if (constructorinfo.isabstract) { p.appendkeywordcsharp("abstract"); p.appendspaces(); } p.appendclasscsharp(constructorinfo.declaringtype.name); p.appendtext("("); bool isfirst = true; foreach (var parameterinfo in constructorinfo.getparameters()) { if (!isfirst) { p.appendtext(", "); } dispalytype(parameterinfo.parametertype, p); p.appendspaces(); p.appendtext(parameterinfo.name); isfirst = false; } p.appendtext(");"); p.appendcarriagereturn(); return true; } static bool dealmethod(methodinfo method, xwpfparagraph p, int spacecount) { if (!(method.ispublic || method.isfamily)) { return false; } //if (method.isprivate) //{ // return false; //} if (method.name.startswith("get_") || method.name.startswith("set_")) { return false; } p.appendspaces(spacecount); if (method.name == "finalize") { p.appendtext("~"); p.appendclasscsharp(method.declaringtype.name); p.appendtext("();"); p.appendcarriagereturn(); return true; } if (method.ispublic) { p.appendkeywordcsharp("public"); } else if (method.isfamily) { p.appendkeywordcsharp("protected"); } else if (method.isassembly) { p.appendkeywordcsharp("internal"); } else if (method.isfamilyandassembly) { p.appendkeywordcsharp("internal protected"); } p.appendspaces(); if (method.isstatic) { p.appendkeywordcsharp("static"); p.appendspaces(); } // a. override parent class method, // b.implement a interface // both have final & virtual keywords bool issealed = false; if (method.isfinal && method != method.getbasedefinition()) { p.appendkeywordcsharp("sealed"); p.appendspaces(); issealed = true; } if (method.isvirtual) { if (method != method.getbasedefinition()) { p.appendkeywordcsharp("override"); } else { if (!method.isfinal) { p.appendkeywordcsharp("virtual"); } } p.appendspaces(); } if (method.isabstract) { p.appendkeywordcsharp("abstract"); p.appendspaces(); } dispalytype(method.returntype, p); p.appendspaces(); p.appendtext(method.name); p.appendtext("("); bool isfirst = true; foreach (var parameterinfo in method.getparameters()) { if (!isfirst) { p.appendtext(", "); } if (parameterinfo.isout) { p.appendkeywordcsharp("out"); p.appendspaces(); } else if (parameterinfo.isin) { p.appendkeywordcsharp("in"); p.appendspaces(); } else if (parameterinfo.parametertype.isbyref) { p.appendkeywordcsharp("ref"); p.appendspaces(); } dispalytype(parameterinfo.parametertype, p); p.appendspaces(); p.appendtext(parameterinfo.name); if (isshowparameterdefaultvalue && parameterinfo.hasdefaultvalue) { p.appendspaces(); p.appendtext("="); p.appendspaces(); if (parameterinfo.defaultvalue == null) { p.appendtext("null"); } else if (parameterinfo.parametertype == typeof(string)) { p.appendtext("\""); p.appendtext(parameterinfo.defaultvalue.tostring()); p.appendtext("\""); } else if (parameterinfo.parametertype == typeof(char)) { p.appendtext("'"); p.appendtext(parameterinfo.defaultvalue.tostring()); p.appendtext("'"); } else if (parameterinfo.parametertype.isenum) { dispalytype(parameterinfo.parametertype, p); p.appendtext("."); p.appendtext(parameterinfo.defaultvalue.tostring()); } else { p.appendtext(parameterinfo.defaultvalue.tostring()); } } isfirst = false; } p.appendtext(");"); p.appendcarriagereturn(); return true; } static void dispalytype(type type, xwpfparagraph p) { // nullable<> need change to like int? if (type.isgenerictype && type.getgenerictypedefinition() == typeof(nullable<>)) { dispalytype(type.getgenericarguments()[0], p); p.appendtext("?"); return; } var typename = type.name; if (typename.contains("`")) { typename = typename.substring(0, typename.lastindexof('`')); } typename = changetonormalname(typename); p.appendclasscsharp(typename); if (type.isgenerictype) { p.appendtext("<"); bool isfisrt = true; foreach (var genericargumenttype in type.getgenericarguments()) { if (!isfisrt) { p.appendtext(", "); } dispalytype(genericargumenttype, p); isfisrt = false; } p.appendtext(">"); } } static string changetonormalname(string typename) { typename = typename.trimend('&'); switch (typename) { case "void": return "void"; case "object": return "object"; case "string": return "string"; case "boolean": return "bool"; case "byte": return "byte"; case "char": return "char"; case "int16": return "short"; case "int32": return "int"; case "int64": return "long"; case "single": return "float"; case "double": return "double"; default: return typename; } } static xwpfparagraph dealenum(type type, xwpfdocument doc, int spacecount) { if (type.isnestedprivate) { return null; } var p = doc.createparagraph(); if (type.getcustomattribute<flagsattribute>() != null) { p.appendspaces(spacecount); p.appendtext("["); p.appendkeywordcsharp("flags"); p.appendtext("]"); p.appendcarriagereturn(); } p.appendspaces(spacecount); if (type.ispublic) { p.appendkeywordcsharp("public"); } else if (type.isnestedassembly) { p.appendkeywordcsharp("internal"); } p.appendspaces(); p.appendkeywordcsharp("enum"); p.appendspaces(); p.appendclasscsharp(type.name); var tempspacecount = spacecount;// - spaceincreasecountofeachlevel > 0 ? spacecount - spaceincreasecountofeachlevel : 0; p.appendcarriagereturn(); p.appendspaces(tempspacecount); p.appendtext("{"); p.appendcarriagereturn(); foreach (var enumname in type.getenumnames()) { p.appendspaces(spacecount + spaceincreasecountofeachlevel); p.appendtext(enumname); p.appendtext(","); p.appendcarriagereturn(); } p.appendspaces(tempspacecount); p.appendtext("}"); return p; } static xwpfparagraph dealstruct(type type, xwpfdocument doc, int spacecount) { if (!type.ispublic) { return null; } //if (type.isnestedprivate) //{ // return null; //} if (type.name.startswith("<")) { return null; } var p = doc.createparagraph(); p.appendspaces(spacecount); if (type.ispublic) { p.appendkeywordcsharp("public"); } else { p.appendkeywordcsharp("internal"); } p.appendspaces(); p.appendkeywordcsharp("struct"); p.appendspaces(); p.appendclasscsharp(type.name); bool isfisrtinterface = true; foreach (var interfacetype in type.getinterfaces()) { if (isfisrtinterface) { p.appendtext(" : "); } else { p.appendtext(", "); } dispalytype(interfacetype, p); isfisrtinterface = false; } p.appendcarriagereturn(); var tempspacecount = spacecount;// - spaceincreasecountofeachlevel > 0 ? spacecount - spaceincreasecountofeachlevel : 0; p.appendspaces(tempspacecount); p.appendtext("{"); p.appendcarriagereturn(); bool hasaddedsomething = false; foreach (var filedinfo in type.getfields(bindingflags)) { if (!filedinfo.ispublic && !filedinfo.isfamily) { continue; } dealfieldinfo(filedinfo, p, spacecount + spaceincreasecountofeachlevel); hasaddedsomething = true; } if (hasaddedsomething) p.appendcarriagereturn(); hasaddedsomething = false; foreach (constructorinfo constructorinfo in type.getconstructors(bindingflags.nonpublic | bindingflags.public | bindingflags.instance)) { if (dealconstructor(constructorinfo, p, spacecount + spaceincreasecountofeachlevel) == true) { hasaddedsomething = true; } } if (hasaddedsomething) p.appendcarriagereturn(); hasaddedsomething = false; foreach (var propertyinfo in type.getproperties(bindingflags)) { if (dealproperty(propertyinfo, p, spacecount + spaceincreasecountofeachlevel) == true) { hasaddedsomething = true; } } if (hasaddedsomething) p.appendcarriagereturn(); hasaddedsomething = false; foreach (methodinfo method in type.getmethods(bindingflags)) { if (dealmethod(method, p, spacecount + spaceincreasecountofeachlevel) == true) { hasaddedsomething = true; } } if (hasaddedsomething) p.appendcarriagereturn(); p.removerun(p.runs.count - 1); p.appendspaces(tempspacecount); p.appendtext("}"); return p; } static accessormodifier getaccessormodifier(methodinfo method) { if (method.ispublic) { return accessormodifier.public; } if (method.isassembly) { return accessormodifier.internal; } if (method.isfamily) { return accessormodifier.protected; } if (method.isfamilyorassembly) { return accessormodifier.internalprotected; } return accessormodifier.private; } static accessormodifier gethighaccessormodifier(accessormodifier? a, accessormodifier? b) { // a or b at least have one value if (a.hasvalue && b.hasvalue) { return (accessormodifier)math.max((int)a.value, (int)b.value); } else if (a.hasvalue == false) { return b.value; } else { return a.value; } } static string accessormodifiertostring(accessormodifier accessormodifier) { switch (accessormodifier) { case accessormodifier.public: return "public"; case accessormodifier.internal: return "internal"; case accessormodifier.protected: return "protected"; break; case accessormodifier.internalprotected: return "internal protected"; case accessormodifier.private: return "private"; default: return ""; } } private enum accessormodifier { public = 100, protected = 95, internal = 90, internalprotected = 80, private = 70, } } internal static class nopiextension { internal static xwpfrun appendtext(this xwpfparagraph paragraph, string text, string color = "000000") { xwpfrun run = paragraph.createrun(); run.settext(text); run.setcolor(color); return run; } internal static xwpfrun appendspaces(this xwpfparagraph paragraph, int spacecount = 1) { return paragraph.appendtext(new string(' ', spacecount)); } internal static xwpfrun appendkeywordcsharp(this xwpfparagraph paragraph, string text) { return paragraph.appendtext(text, interfaceexporthelper.csharpkeywordcolor); } internal static xwpfrun appendclasscsharp(this xwpfparagraph paragraph, string text) { return paragraph.appendtext(text, interfaceexporthelper.csharpclasscolor); } internal static xwpfrun appendcarriagereturn(this xwpfparagraph paragraph) { var run = paragraph.createrun(); run.addcarriagereturn(); return run; } }
用法
var assemblypath = @"d:\client\bin\debug\client.dll"; var outputdocpath = @"d:\client.docx"; interfaceexporthelper.isshowparameterdefaultvalue = false; interfaceexporthelper.separateinterfacetodocument(assemblypath, outputdocpath);