20

我正在使用 xml 注释来记录我的组件的公共以及内部和私有成员。我想将生成的文档 xml 文件与组件程序集打包在一起,以便在最终产品中启用“丰富”(例如,带有方法、异常和参数描述)Visual Studio Intellisense。问题在于 C# 编译器为所有内容(包括内部类、方法、内部枚举的私有字段等)创建文档条目,并且似乎没有切换到“仅公共成员”模式。

现在我不想检查超过 50 个文件,每个文件都有 XX 方法,并删除私人和内部成员的所有评论。即使我这样做了,我也可能不会在自动生成的资源文件上取得太大的成功,因为这些强类型资源类是自动注释的并且是非公开的。

我的问题是:是否有一些我忽略的选项/标志?如果不是,是否有一些工具可以帮助将公共成员与其他成员区分开来(在我开始编写代码之前)?

4

6 回答 6

7

SandCastle Help File Builder 有一个选项可以重新创建 xml 文件,其中仅包含为方法、属性等配置的访问模式...

唯一的“缺点”是您必须生成文档。

编辑

因为很久以前我忘记了我向 SHFB 添加了一个“组件”来生成 XML。

好消息是这个组件包含在 SHFB 中。

您必须将“智能感知组件”添加到 SHFB 项目。然后它将根据配置的 SHFB 项目生成 XML。

更多信息:SHFB 中的 Intellisense 组件

于 2011-06-01T19:24:53.653 回答
4

eazfuscator内部有一个工具可以删除非公开文档。你可以在这里看到一个例子

于 2011-06-01T04:13:06.383 回答
3

我对此进行了一些思考,并决定改变解决这个特定问题的方式。而不是在尝试解析 XML 文档表示法的程序集中查找类型/成员。我决定简单地为公共 API 构建一个字符串集(XML 文档表示法),然后可以使用它来测试成员是否不是公共的。

这真的很简单。向 发送程序集,XmlDocumentationStringSet它将构建公共 API 的字符串集并删除不公开的元素。

static void Main(string[] args)
{
    var el = XElement.Load("ConsoleApplication18.XML");

    // obviously, improve this if necessary (might not work like this if DLL isn't already loaded)
    // you can use file paths
    var assemblyName = el.Descendants("assembly").FirstOrDefault();
    var assembly = Assembly.ReflectionOnlyLoad(assemblyName.Value);

    var stringSet = new XmlDocumentationStringSet(assembly);

    foreach (var member in el.Descendants("member").ToList()) // .ToList enables removing while traversing
    {
        var attr = member.Attribute("name");
        if (attr == null)
        {
            continue;
        }
        if (!stringSet.Contains(attr.Value))
        {
            member.Remove();
        }
    }

    el.Save("ConsoleApplication18-public.XML");
}

这是构建 XML 文档名称的类(它有点大,但我想我还是在这里发布了整个源代码):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace ConsoleApplication18
{
    public class XmlDocumentationStringSet : IEnumerable<string>
    {
        private HashSet<string> stringSet = new HashSet<string>(StringComparer.Ordinal);

        public XmlDocumentationStringSet(Assembly assembly)
        {
            AddRange(assembly.GetExportedTypes());
        }

        public bool Contains(string name)
        {
            return stringSet.Contains(name);
        }

        /// <summary>
        /// Heelloasdasdasd
        /// </summary>
        /// <param name="types"></param>
        public void AddRange(IEnumerable<Type> types)
        {
            foreach (var type in types)
            {
                Add(type);
            }
        }

        public void Add(Type type)
        {
            // Public API only
            if (!type.IsVisible)
            {
                return;
            }
            var members = type.GetMembers(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
            foreach (var member in members)
            {
                Add(type, member);
            }
        }

        StringBuilder sb = new StringBuilder();

        private void Add(Type type, MemberInfo member)
        {
            Type nestedType = null;

            sb.Length = 0;

            switch (member.MemberType)
            {
                case MemberTypes.Constructor:
                    sb.Append("M:");
                    AppendConstructor(sb, (ConstructorInfo)member);
                    break;
                case MemberTypes.Event:
                    sb.Append("E:");
                    AppendEvent(sb, (EventInfo)member);
                    break;
                case MemberTypes.Field:
                    sb.Append("F:");
                    AppendField(sb, (FieldInfo)member);
                    break;
                case MemberTypes.Method:
                    sb.Append("M:");
                    AppendMethod(sb, (MethodInfo)member);
                    break;
                case MemberTypes.NestedType:
                    nestedType = (Type)member;
                    if (IsVisible(nestedType))
                    {
                        sb.Append("T:");
                        AppendNestedType(sb, (Type)member);
                    }
                    break;
                case MemberTypes.Property:
                    sb.Append("P:");
                    AppendProperty(sb, (PropertyInfo)member);
                    break;
            }

            if (sb.Length > 0)
            {
                stringSet.Add(sb.ToString());
            }

            if (nestedType != null)
            {
                Add(nestedType);
            }
        }

        private bool IsVisible(Type nestedType)
        {
            return nestedType.IsVisible;
        }

        private void AppendProperty(StringBuilder sb, PropertyInfo propertyInfo)
        {
            if (!IsVisible(propertyInfo))
            {
                sb.Length = 0;
                return;
            }
            AppendType(sb, propertyInfo.DeclaringType);
            sb.Append('.').Append(propertyInfo.Name);
        }

        private bool IsVisible(PropertyInfo propertyInfo)
        {
            var getter = propertyInfo.GetGetMethod();
            var setter = propertyInfo.GetSetMethod();
            return (getter != null && IsVisible(getter)) || (setter != null && IsVisible(setter));
        }

        private void AppendNestedType(StringBuilder sb, Type type)
        {
            AppendType(sb, type.DeclaringType);
        }

        private void AppendMethod(StringBuilder sb, MethodInfo methodInfo)
        {
            if (!IsVisible(methodInfo) || (methodInfo.IsHideBySig && methodInfo.IsSpecialName))
            {
                sb.Length = 0;
                return;
            }
            AppendType(sb, methodInfo.DeclaringType);
            sb.Append('.').Append(methodInfo.Name);
            AppendParameters(sb, methodInfo.GetParameters());
        }

        private bool IsVisible(MethodInfo methodInfo)
        {
            return methodInfo.IsFamily || methodInfo.IsPublic;
        }

        private void AppendParameters(StringBuilder sb, ParameterInfo[] parameterInfo)
        {
            if (parameterInfo.Length == 0)
            {
                return;
            }
            sb.Append('(');
            for (int i = 0; i < parameterInfo.Length; i++)
            {
                if (i > 0)
                {
                    sb.Append(',');
                }
                var p = parameterInfo[i];
                AppendType(sb, p.ParameterType);
            }
            sb.Append(')');
        }

        private void AppendField(StringBuilder sb, FieldInfo fieldInfo)
        {
            if (!IsVisible(fieldInfo))
            {
                sb.Length = 0;
                return;
            }
            AppendType(sb, fieldInfo.DeclaringType);
            sb.Append('.').Append(fieldInfo.Name);
        }

        private bool IsVisible(FieldInfo fieldInfo)
        {
            return fieldInfo.IsFamily || fieldInfo.IsPublic;
        }

        private void AppendEvent(StringBuilder sb, EventInfo eventInfo)
        {
            if (!IsVisible(eventInfo))
            {
                sb.Length = 0;
                return;
            }
            AppendType(sb, eventInfo.DeclaringType);
            sb.Append('.').Append(eventInfo.Name);
        }

        private bool IsVisible(EventInfo eventInfo)
        {
            return true; // hu?
        }

        private void AppendConstructor(StringBuilder sb, ConstructorInfo constructorInfo)
        {
            if (!IsVisible(constructorInfo))
            {
                sb.Length = 0;
                return;
            }
            AppendType(sb, constructorInfo.DeclaringType);
            sb.Append('.').Append("#ctor");
            AppendParameters(sb, constructorInfo.GetParameters());
        }

        private bool IsVisible(ConstructorInfo constructorInfo)
        {
            return constructorInfo.IsFamily || constructorInfo.IsPublic;
        }

        private void AppendType(StringBuilder sb, Type type)
        {
            if (type.DeclaringType != null)
            {
                AppendType(sb, type.DeclaringType);
                sb.Append('.');
            }
            else if (!string.IsNullOrEmpty(type.Namespace))
            {
                sb.Append(type.Namespace);
                sb.Append('.');
            }
            sb.Append(type.Name);
            if (type.IsGenericType && !type.IsGenericTypeDefinition)
            {
                // Remove "`1" suffix from type name
                while (char.IsDigit(sb[sb.Length - 1]))
                    sb.Length--;
                sb.Length--;
                {
                    var args = type.GetGenericArguments();
                    sb.Append('{');
                    for (int i = 0; i < args.Length; i++)
                    {
                        if (i > 0)
                        {
                            sb.Append(',');
                        }
                        AppendType(sb, args[i]);
                    }
                    sb.Append('}');
                }
            }
        }

        public IEnumerator<string> GetEnumerator()
        {
            return stringSet.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
}

哦,我还没有弄清楚如何处理事件,在这个例子中它们总是可见的。

于 2011-06-03T15:44:40.037 回答
2

@约翰·莱德格伦

我有同样的要求,我找到了你的代码缺失部分的答案。一个事件有 2 个方法,添加和删除,如果其中任何一个是公共的,则被认为是公共的。所以它会是这样的:

private bool IsVisible(EventInfo eventInfo)
{
    return eventInfo.GetAddMethod(false) != null
        || eventInfo.GetRemoveMethod(false) != null;
}

虽然我想不出任何理由为什么一个会公开而不是另一个。

于 2012-10-10T10:40:18.640 回答
0

您使用什么工具来生成文档?我使用 Sandcastle,这使您可以选择通过可访问性包含的成员。

一般来说,似乎期望 XML 将包含所有可能需要的信息,并由处理工具从中选择所需的信息。

于 2009-03-08T22:41:56.093 回答
0

我面临着同样的问题。SHFB 慢得要命,因为我们有另一个文档代码库,我们不需要它来为我们生成文档。

我最终使用XMLStarlet加上一个单独的内部类命名空间。例如,我所有的内部类都将驻留在MyCompany.MyProduct.Internal. 然后我可以使用一个简单的命令

xml ed -L -d "//member[contains(@name, 'MyCompany.MyProduct.Internal')]" MyProduct.xml

清理 XML。这当然不是万无一失的——它不包括公共类中的内部成员,并且确实需要一些纪律来记住将内部类放入内部。但这是对我有用的最干净、侵入性最小的方法。它也是一个独立的 EXE 文件,很容易签入构建服务器,毫不费力。

于 2011-07-04T07:45:17.010 回答