以编程方式在运行时获取摘要注释

Ria*_*ers 48 c# asp.net reflection documentation

我正在寻找一种方法来以编程方式获取ASP.net中方法的Xml-comments的摘要部分.

我查看过以前的相关帖子,但他们没有提供在Web环境中这样做的方法.

我不能使用任何第三方应用程序,并且由于Web环境,Visual Studio插件也没有多大用处.

我找到的最接近工作解决方案的是JimBlackler项目,但它只适用于DLL.

当然,诸如"提供.CS文件,获取XML文档"之类的东西将是最佳的.


现在的情况

我有一个Web服务,并尝试为它动态生成文档.

阅读方法和属性很简单,但是获取每种方法的摘要会让我失望.

/// <summary>
/// This Is what I'm trying to read
/// </summary>
public class SomeClass()
{
    /// <summary>
    /// This Is what I'm trying to read
    /// </summary>
    public void SomeMethod()
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

小智 40

解决方法 - 将Program.DLL/EXE上的反射与Program.XML文件一起使用

如果你看一下Visual Studio生成的兄弟.XML文件,你会发现/ members/member有一个相当平坦的层次结构.您所要做的就是通过MethodInfo对象从DLL中获取每个方法.获得此对象后,转到XML并使用XPATH获取包含此方法的XML文档的成员.

成员之前是一封信.方法的XML doc前面是"M:",类是"T:"等.

加载您的兄弟XML

string docuPath = dllPath.Substring(0, dllPath.LastIndexOf(".")) + ".XML";

if (File.Exists(docuPath))
{
  _docuDoc = new XmlDocument();
  _docuDoc.Load(docuPath);
}
Run Code Online (Sandbox Code Playgroud)

使用此xpath获取表示方法XML文档的成员

string path = "M:" + mi.DeclaringType.FullName + "." + mi.Name;

XmlNode xmlDocuOfMethod = _docuDoc.SelectSingleNode(
    "//member[starts-with(@name, '" + path + "')]");
Run Code Online (Sandbox Code Playgroud)

现在扫描所有行"///"的子节点有时///摘要包含额外的空格,如果这个困扰使用此删除

var cleanStr = Regex.Replace(row.InnerXml, @"\s+", " ");
Run Code Online (Sandbox Code Playgroud)

  • 这很棒,虽然你可以通过使用`Path.ChangeExtension(dllPath,".XML")来大大简化你的第一行. (14认同)

Sec*_*att 29

XML摘要不存储在.NET程序集中 - 它可选地作为构建的一部分写入XML文件(假设您使用的是Visual Studio).

因此,无法通过对已编译的.NET程序集(.EXE或.DLL)的反射来"拉出"每个方法的XML摘要 - 因为数据根本不适合您提取.如果需要数据,则必须指示构建环境在构建过程中输出XML文件,并在运行时解析这些XML文件以获取摘要信息.

  • 如何“指示构建环境在构建过程中输出 XML 文件”? (2认同)

Net*_*ity 22

您可以使用System.ComponentModel.DataAnnotations.DisplayAttribute属性"记录"您的方法,例如

[Display(Name = "Foo", Description = "Blah")]
void Foo()
{
}
Run Code Online (Sandbox Code Playgroud)

然后使用反射在运行时拉出描述.


Ben*_*pka 12

由@OleksandrIeremenko 在此线程上发布的已删除帖子链接到本文https://jimblackler.net/blog/?p=49,这是我的解决方案的基础。

下面是对 Jim Blackler 代码的修改,使扩展方法脱离 MemberInfo 和 Type 对象,并添加了返回摘要文本或空字符串(如果不可用)的代码。

用法

var typeSummary = typeof([Type Name]).GetSummary();
var methodSummary = typeof([Type Name]).GetMethod("[Method Name]").GetSummary();
Run Code Online (Sandbox Code Playgroud)

扩展类

/// <summary>
/// Utility class to provide documentation for various types where available with the assembly
/// </summary>
public static class DocumentationExtensions
{
    /// <summary>
    /// Provides the documentation comments for a specific method
    /// </summary>
    /// <param name="methodInfo">The MethodInfo (reflection data ) of the member to find documentation for</param>
    /// <returns>The XML fragment describing the method</returns>
    public static XmlElement GetDocumentation(this MethodInfo methodInfo)
    {
        // Calculate the parameter string as this is in the member name in the XML
        var parametersString = "";
        foreach (var parameterInfo in methodInfo.GetParameters())
        {
            if (parametersString.Length > 0)
            {
                parametersString += ",";
            }

            parametersString += parameterInfo.ParameterType.FullName;
        }

        //AL: 15.04.2008 ==> BUG-FIX remove “()” if parametersString is empty
        if (parametersString.Length > 0)
            return XmlFromName(methodInfo.DeclaringType, 'M', methodInfo.Name + "(" + parametersString + ")");
        else
            return XmlFromName(methodInfo.DeclaringType, 'M', methodInfo.Name);
    }

    /// <summary>
    /// Provides the documentation comments for a specific member
    /// </summary>
    /// <param name="memberInfo">The MemberInfo (reflection data) or the member to find documentation for</param>
    /// <returns>The XML fragment describing the member</returns>
    public static XmlElement GetDocumentation(this MemberInfo memberInfo)
    {
        // First character [0] of member type is prefix character in the name in the XML
        return XmlFromName(memberInfo.DeclaringType, memberInfo.MemberType.ToString()[0], memberInfo.Name);
    }
    /// <summary>
    /// Returns the Xml documenation summary comment for this member
    /// </summary>
    /// <param name="memberInfo"></param>
    /// <returns></returns>
    public static string GetSummary(this MemberInfo memberInfo)
    {
        var element = memberInfo.GetDocumentation();
        var summaryElm = element?.SelectSingleNode("summary");
        if (summaryElm == null) return "";
        return summaryElm.InnerText.Trim();
    }

    /// <summary>
    /// Provides the documentation comments for a specific type
    /// </summary>
    /// <param name="type">Type to find the documentation for</param>
    /// <returns>The XML fragment that describes the type</returns>
    public static XmlElement GetDocumentation(this Type type)
    {
        // Prefix in type names is T
        return XmlFromName(type, 'T', "");
    }

    /// <summary>
    /// Gets the summary portion of a type's documenation or returns an empty string if not available
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    public static string GetSummary(this Type type)
    {
        var element = type.GetDocumentation();
        var summaryElm = element?.SelectSingleNode("summary");
        if (summaryElm == null) return "";
        return summaryElm.InnerText.Trim();
    }

    /// <summary>
    /// Obtains the XML Element that describes a reflection element by searching the 
    /// members for a member that has a name that describes the element.
    /// </summary>
    /// <param name="type">The type or parent type, used to fetch the assembly</param>
    /// <param name="prefix">The prefix as seen in the name attribute in the documentation XML</param>
    /// <param name="name">Where relevant, the full name qualifier for the element</param>
    /// <returns>The member that has a name that describes the specified reflection element</returns>
    private static XmlElement XmlFromName(this Type type, char prefix, string name)
    {
        string fullName;

        if (string.IsNullOrEmpty(name))
            fullName = prefix + ":" + type.FullName;
        else
            fullName = prefix + ":" + type.FullName + "." + name;

        var xmlDocument = XmlFromAssembly(type.Assembly);

        var matchedElement = xmlDocument["doc"]["members"].SelectSingleNode("member[@name='" + fullName + "']") as XmlElement;

        return matchedElement;
    }

    /// <summary>
    /// A cache used to remember Xml documentation for assemblies
    /// </summary>
    private static readonly Dictionary<Assembly, XmlDocument> Cache = new Dictionary<Assembly, XmlDocument>();

    /// <summary>
    /// A cache used to store failure exceptions for assembly lookups
    /// </summary>
    private static readonly Dictionary<Assembly, Exception> FailCache = new Dictionary<Assembly, Exception>();

    /// <summary>
    /// Obtains the documentation file for the specified assembly
    /// </summary>
    /// <param name="assembly">The assembly to find the XML document for</param>
    /// <returns>The XML document</returns>
    /// <remarks>This version uses a cache to preserve the assemblies, so that 
    /// the XML file is not loaded and parsed on every single lookup</remarks>
    public static XmlDocument XmlFromAssembly(this Assembly assembly)
    {
        if (FailCache.ContainsKey(assembly))
        {
            throw FailCache[assembly];
        }

        try
        {

            if (!Cache.ContainsKey(assembly))
            {
                // load the docuemnt into the cache
                Cache[assembly] = XmlFromAssemblyNonCached(assembly);
            }

            return Cache[assembly];
        }
        catch (Exception exception)
        {
            FailCache[assembly] = exception;
            throw;
        }
    }

    /// <summary>
    /// Loads and parses the documentation file for the specified assembly
    /// </summary>
    /// <param name="assembly">The assembly to find the XML document for</param>
    /// <returns>The XML document</returns>
    private static XmlDocument XmlFromAssemblyNonCached(Assembly assembly)
    {
        var assemblyFilename = assembly.Location;
   
        if (!string.IsNullOrEmpty(assemblyFilename))
        {
            StreamReader streamReader;

            try
            {
                streamReader = new StreamReader(Path.ChangeExtension(assemblyFilename, ".xml"));
            }
            catch (FileNotFoundException exception)
            {
                throw new Exception("XML documentation not present (make sure it is turned on in project properties when building)", exception);
            }

            var xmlDocument = new XmlDocument();
            xmlDocument.Load(streamReader);
            return xmlDocument;
        }
        else
        {
            throw new Exception("Could not ascertain assembly filename", null);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 在 VisualStudio 中,默认情况下禁用生成文档 xml 文件。但是,我们可以启用它:右键单击要启用 xml 文档生成的项目 &gt; 属性 &gt; 构建选项卡 &gt; 选中“Xml 文档文件”复选框。或者只需在 `YourProject.csproj 中设置 '&lt;PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"&gt; &lt;DocumentationFile&gt;AddressAndNameOfYourProject.xml&lt;/DocumentationFile&gt; &lt;/PropertyGroup&gt;` ` 文件。 (3认同)

Vla*_*nko 9

您可以使用Namotion.Reflection NuGet 包来获取这些信息:

string summary = typeof(Foo).GetXmlDocsSummary();
Run Code Online (Sandbox Code Playgroud)