C# 在运行时以编程方式获取摘要注释
我正在寻找一种在ASP.net中以编程方式获取方法的Xml注释摘要部分的方法 我已经看过了以前的相关文章,但它们并没有提供在web环境中这样做的方法 我不能使用任何第三方应用程序,而且由于网络环境的原因,VisualStudio插件也没有多大用处 我发现的最接近工作解决方案是JimBlackler项目,但它只在DLL上工作 当然,像“supply.CS文件,获取XML文档”这样的东西将是最佳选择C# 在运行时以编程方式获取摘要注释,c#,asp.net,reflection,documentation,C#,Asp.net,Reflection,Documentation,我正在寻找一种在ASP.net中以编程方式获取方法的Xml注释摘要部分的方法 我已经看过了以前的相关文章,但它们并没有提供在web环境中这样做的方法 我不能使用任何第三方应用程序,而且由于网络环境的原因,VisualStudio插件也没有多大用处 我发现的最接近工作解决方案是JimBlackler项目,但它只在DLL上工作 当然,像“supply.CS文件,获取XML文档”这样的东西将是最佳选择 现状 我有一个web服务,并试图为它动态生成文档 阅读方法和属性很容易,但是获得每个方法的摘要让我
现状 我有一个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()
{
}
}
//
///这就是我想读的
///
公共类SomeClass()
{
///
///这就是我想读的
///
公共方法()
{
}
}
XML摘要不存储在.NET程序集中-作为构建的一部分,它可以选择性地写入XML文件(假设您使用的是Visual Studio)
因此,无法通过在编译的.NET程序集(或.EXE或.DLL)上的反射来“提取”每个方法的XML摘要,因为数据根本不在那里供您提取。如果需要数据,则必须指示生成环境将XML文件作为生成过程的一部分输出,并在运行时解析这些XML文件以获取摘要信息。您可以使用
System.ComponentModel.DataAnnotations.DisplayAttribute
属性,例如
[Display(Name = "Foo", Description = "Blah")]
void Foo()
{
}
然后在运行时使用反射来提取描述。一种解决方法-将Program.DLL/EXE上的反射与Program.XML文件一起使用 如果您查看一下VisualStudio生成的sibling.XML文件,就会发现/members/member是一个相当扁平的层次结构。 您所要做的就是通过MethodInfo对象从DLL获取每个方法。一旦有了这个对象,就转向XML并使用XPATH获取包含该方法的XML文档的成员 成员前面有一封信。方法的XML文档前面有“M:”类前面有“T:”等 加载您的同级XML
string docuPath = dllPath.Substring(0, dllPath.LastIndexOf(".")) + ".XML";
if (File.Exists(docuPath))
{
_docuDoc = new XmlDocument();
_docuDoc.Load(docuPath);
}
使用此xpath获取表示方法XML docu的成员
string path = "M:" + mi.DeclaringType.FullName + "." + mi.Name;
XmlNode xmlDocuOfMethod = _docuDoc.SelectSingleNode(
"//member[starts-with(@name, '" + path + "')]");
现在扫描所有“/”行的子节点
有时///摘要包含额外的空格,如果有麻烦,请使用它来删除
var cleanStr = Regex.Replace(row.InnerXml, @"\s+", " ");
如果您有权访问试图获取注释的源代码,那么可以使用。它基本上允许您访问所有中间编译器元数据,并且您可以使用它做任何您想做的事情 这比其他人的建议要复杂一点,但根据你的需要,可能是一种选择 它看起来有一个类似的代码示例。您可以查看-sourcefornuget-NSwag.CodeGeneration-它还可以获得摘要和用法
var generator = new WebApiAssemblyToSwaggerGenerator(settings);<br/>
var swaggerService = generator.GenerateForController("namespace.someController");<br/>
// string with comments <br/>
var swaggerJson = swaggerService.ToJson();
var生成器=新的webapisassembly-toswaggergenerator(设置)
var swaggerService=generator.GenerateForController(“namespace.someController”)
//带注释的字符串
var swaggerJson=swaggerService.ToJson();
(针对您的dll尝试ILSPY反编译器,检查代码和注释)一篇由@OleksandrIeremenko在该线程上发表的已删除帖子链接到本文,这篇文章是我的解决方案的基础 下面是对Jim Blackler的代码的修改,它使扩展方法脱离MemberInfo和Type对象,并添加返回摘要文本或空字符串(如果不可用)的代码 用法 扩展类
/// <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);
}
}
}
//
///实用程序类,用于提供部件可用的各种类型的文档
///
公共静态类文档扩展
{
///
///提供特定方法的文档注释
///
///要查找文档的成员的MethodInfo(反射数据)
///描述该方法的XML片段
公共静态XmlElement GetDocumentation(此MethodInfo MethodInfo)
{
//计算参数字符串,因为它位于XML中的成员名称中
var参数字符串=”;
foreach(methodInfo.GetParameters()中的var parameterInfo)
{
如果(参数字符串长度>0)
{
参数字符串+=“,”;
}
parametersString+=parameterInfo.ParameterType.FullName;
}
//AL:15.04.2008==>BUG-FIX删除“()”如果参数字符串为空
如果(参数字符串长度>0)
返回XmlFromName(methodInfo.DeclaringType,'M',methodInfo.Name+”(“+参数字符串+”);
其他的
返回XmlFromName(methodInfo.DeclaringType'M',methodInfo.Name);
}
///
///提供特定成员的文档注释
///
///MemberInfo(反射数据)或要查找文档的成员
///描述成员的XML片段
公共静态XmlElement GetDocumentation(此MemberInfo MemberInfo)
{
//成员类型的第一个字符[0]是XML中名称中的前缀字符
返回XmlFromName(memberInfo.DeclaringType,memberInfo.MemberType.ToString()[0],memberInfo.Name);
}
///
///返回此成员的Xml文档摘要注释
///
///
///
公共静态字符串GetSummary(此MemberInfo MemberInfo)
{
var元素=memberInfo.GetDocumentation();
var summaryElm=元素?.SelectSingleNode(“摘要”);
如果(summaryElm==null)返回“”;
返回summaryElm.InnerText.Trim();
}
///
///提供特定类型的文档注释
///
///键入以查找其文档
///描述
/// <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);
}
}
}
string summary = typeof(Foo).GetXmlDocsSummary();