公司產品對外公佈的介面, 評審後才能發佈, 然後要求生成介面文檔(去除註釋, 這樣更能檢查介面命名是否合理). 之前用的是微軟的一個免費工具, 但好久都沒有更新了, 對新一點的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