标记通过反射调用的方法的最佳实践?


11

我们的软件有几个类,应该通过反射来动态找到。这些类都有一个带有特定签名的构造函数,反射代码通过该签名实例化对象。
但是,当有人检查是否引用了该方法时(例如,通过Visual Studio Code Lens),则不计入通过反射进行的引用。人们可能会错过他们的引用,并删除(或更改)表面上未使用的方法。

我们应该如何标记/记录打算通过反射调用的方法?

理想情况下,应以同事和Visual Studio / Roslyn以及其他自动化工具都“看到”该方法旨在通过反射调用的方式进行标记。

我知道我们可以使用两个选项,但都不太令人满意。由于Visual Studio找不到引用,因此:

  • 使用自定义属性,并使用此属性标记构造函数。
    • 问题在于,属性属性不能是方法引用,因此构造方法仍将显示为具有0个引用。
    • 不熟悉custom属性的同事可能会忽略它。
    • 我当前方法的一个优点是反射部分可以使用该属性来查找应调用的构造函数。
  • 使用注释来证明打算通过反射调用方法/构造函数。
    • 自动化工具会忽略评论(同事也可能会忽略评论)。
    • XML文档注释可以用于具有Visual Studio计数的额外参考方法/构造:
      MyPlugin是它的构造经由反射调用的类。假设调用反射代码搜索采用int参数的构造函数。以下文档使代码透镜显示了具有1个引用的构造函数:
      /// <see cref="MyPlugin.MyPlugin(int)"/> is invoked via reflection

存在哪些更好的选择?
标记要通过反射调用的方法/构造函数的最佳实践是什么?


明确一点,这是针对某种插件系统的,对吗?
whatsisname 2017年

2
您假设您的同事将忽略或错过您所做的一切...您无法防止代码在工作中如此无效。在我看来,记录似乎是一种更容易,更清洁,更便宜和更可取的方式。否则将不存在声明性编程。
Laiv

1
Resharper具有[UsedImplictly]属性。
CodesInChaos

4
我猜Xml doc注释选项可能是您最好的选择。它是简短的自记录文档,不需要任何“ hacks”或其他定义。
布朗

2
xml文档注释的另一票。无论如何,如果您正在创建文档,则应在生成的文档中脱颖而出。
Frank Hileman

Answers:


12

建议解决方案的组合:

  • 使用XML Documentation标签来记录构造函数/方法是通过反射调用的。
    这应该向同事(和我未来的自我)阐明预期的用法。
  • 通过<see>-tag 使用“技巧” 来增加构造函数/方法的引用计数。
    这使得该代码和查找引用表明该构造函数/方法已被引用。
  • 用Resharper的注释 UsedImplicitlyAttribute
    • Resharper是事实上的标准,并且[UsedImplicitly]具有预期的语义。
    • 那些不使用Resharper的用户可以通过NuGet: PM> 安装JetBrains ReSharper注释
      Install-Package JetBrains.Annotations
  • 如果这是私有方法,并且您正在使用Visual Studio的代码分析,请SupressMessageAttribute对消息 使用CA1811: Avoid uncalled private code

例如:

class MyPlugin
{
    /// <remarks>
    /// <see cref="MyPlugin.MyPlugin(int)"/> is called via reflection.
    /// </remarks>
    [JetBrains.Annotations.UsedImplicitly]
    public MyPlugin(int arg)
    {
        throw new NotImplementedException();
    }

    /// <remarks>
    /// <see cref="MyPlugin.MyPlugin(string)"/> is called via reflection.
    /// </remarks>
    [JetBrains.Annotations.UsedImplicitly]
    [System.Diagnostics.CodeAnalysis.SuppressMessage(
        "Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode",
        Justification = "Constructor is called via reflection")]
    private MyPlugin(string arg)
    {
        throw new NotImplementedException();
    }
}

该解决方案将构造函数的预期用途授予了人类读者以及最常与C#和Visual Studio一起使用的3种静态代码分析系统。
缺点是评论和一个或两个注释似乎都有点多余。


请注意,还有MeansImplicitUseAttribute一些可用于使自己的属性UsedImplicitly生效。在适当的情况下,这可以减少很多属性噪音。
Dave Cousineau '18

到JetBrains的链接已损坏。
约翰·扎布罗斯基

5

我从未在.Net项目中遇到过此问题,但对于Java项目,我经常遇到相同的问题。我通常的做法是使用@SuppressWarnings("unused")批注添加注释,以解释原因(记录禁用任何警告的原因是我的标准代码风格的一部分-每当编译器无法解决问题时,我都认为人类很可能会挣扎太)。这样的优点是可以自动确保静态分析工具知道该代码不应直接引用,并为人类读者提供了详细的原因。

Java的C#等效项@SuppressWarningsSuppressMessageAttribute。对于私有方法,您可以使用消息CA1811:避免使用未调用的私有代码;例如:

class MyPlugin
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage(
        "Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode",
        Justification = "Constructor is called via reflection")]
    private MyPlugin(int arg)
    {
        throw new NotImplementedException();
    }
}

(我不知道,但是假定CLI支持类似于我提到的Java的属性;如果有人知道它是什么,请编辑我的答复作为对其的引用...)
Jules


1
我最近发现,执行测试和测量覆盖率(在Java中)是了解代码块是否真正未使用的好方法。然后,我可以将其删除或查看为什么未使用(如果我期望相反的话)。在搜索时,我会更加注意这些评论。
Laiv

存在SuppressMessageAttributemsdn.microsoft.com/en-us/library/…)。最接近的消息是CA1811: Avoid uncalled private codemsdn.microsoft.com/en-us/library/ms182264.aspx);我尚未找到公开代码的消息。
卡斯珀·范·登·伯格

2

记录的替代方法是进行单元测试,以确保反射调用成功运行。

这样,如果有人更改或删除了方法,则您的构建/测试过程应提醒您已损坏某些东西。


0

嗯,没有看到您的代码,听起来像是引入一些继承的好地方。也许这些类的构造函数可以调用的虚拟方法或抽象方法?如果您要标记的方法只是构造函数,那么您实际上是在标记一个类而不是方法,是吗?我过去做过的标记类的事情是创建一个空接口。然后,代码检查工具和重构可以查找实现该接口的类。


通常情况下,真正的继承是必经之路。而且这些类确实是通过继承关联的。但是在继承之外创建一个新实例。此外,由于C#不支持类插槽,因此我依靠静态方法的“继承性”。有一种解决方法,我可以使用,但这超出了此注释的范围。
卡斯珀·范·登·伯格
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.