gpt4 book ai didi

c# - 在 C# 中使用反射检测编译器生成的默认构造函数

转载 作者:太空狗 更新时间:2023-10-29 22:28:58 27 4
gpt4 key购买 nike

我的目标是 .NET 3.5 SP1,我正在使用 CommentChecker为了验证我的 XML 文档,一切正常,直到我进入这样的类(class):

/// <summary>
/// documentation
/// </summary>
public sealed class MyClass {
/// <summary>
/// documentation
/// </summary>
public void Method() {
}
}

在上面的例子中,据我所知,编译器为我的类生成了一个默认构造函数。问题是 CommentChecker 会生成警告,告诉我构造函数缺少注释。

我试图修改程序以检测这种特殊情况并忽略它,但我被卡住了,我已经尝试使用 IsDefined(typeof(CompilerGeneratedAttribute), true) 但那没有用。

简而言之,我如何使用反射检测默认构造函数?

最佳答案

如果您愿意深入了解 IL,那么您可以了解大部分

首先,假设您有 ConstructorInfo您知道无参数的实例,您可以像这样获取方法主体和方法主体的字节(我们将开始构建扩展方法来执行此操作):

public static bool MightBeCSharpCompilerGenerated(
this ConstructorInfo constructor)
{
// Validate parmaeters.
if (constructor == null) throw new ArgumentNullException("constructor");

// If the method is static, throw an exception.
if (constructor.IsStatic)
throw new ArgumentException("The constructor parameter must be an " +
"instance constructor.", "constructor");

// Get the body.
byte[] body = constructor.GetMethodBody().GetILAsByteArray();

您可以拒绝任何没有七个字节的方法体。

    // Feel free to put this in a constant.
if (body.Length != 7) return false;

原因在后面的代码中会很明显。

ECMA-335 (Common Language Infrastructure (CLI) Partitions I to VI) 的 I.8.9.6.6 节中,它声明了 CLS 规则 21:

CLS Rule 21: An object constructor shall call some instance constructor of its base class before any access occurs to inherited instance data. (This does not apply to value types, which need not have constructors.)

这意味着在任何 其他操作完成之前,必须 调用基础构造函数。我们可以在 IL 中检查这一点。 IL 看起来像这样(我将字节值放在 IL 命令之前的括号中):

// Loads "this" on the stack, as the first argument on an instance
// method is always "this".
(0x02) ldarg.0

// No parameters are loaded, but metadata token will be explained.
(0x28) call <metadata token>

我们现在可以开始检查字节了:

    // Check the first two bytes, if they are not the loading of
// the first argument and then a call, it's not
// a call to a constructor.
if (body[0] != 0x02 || body[1] != 0x28) return false;

现在是元数据 token 。 call instruction需要一个方法描述符以元数据标记的形式与构造函数一起传递。这是一个四字节的值,通过 MetadataToken property 公开。在 MemberInfo class 上(ConstructorInfo 从中派生)。

我们可以检查元数据标记是否有效,但是因为我们已经检查了方法体字节数组的长度(七个字节),而且我们知道只有剩下一个字节要检查(前两个操作码 + 四字节元数据标记 = 六个字节),我们不必检查它是否是无参数构造函数;如果有参数,就会有其他操作码将参数压入堆栈,扩展字节数组。

最后,如果在构造函数中什么都没做(表明编译器生成的构造函数只调用基类),ret 指令将在之后发出调用元数据 token :

(0x2A) ret

我们可以这样检查:

    return body[6] == 0x2a;
}

需要说明的是为什么这个方法叫做MightBeCSharpCompilerGenerated,重点是Might

假设您有以下类(class):

public class Base { }
public class Derived : Base { public Derived() { } }

在没有优化的情况下编译时(通常是 DEBUG 模式),C# 编译器会插入一些 nop codes (大概是为了协助调试器)用于 Derived 类,这将导致对 MightBeCSharpCompilerGenerated 的调用返回 false。

但是,当打开优化时(通常是 RELEASE 模式),C# 编译器将发出没有 nop 操作码的七字节方法体,因此它将看起来 Derived 有一个编译器生成的构造函数,尽管它没有。

这就是为什么方法被命名为Might而不是IsHas的原因;它表明可能您需要查看一种方法,但不能确定。换句话说,您永远不会得到假阴性,但如果得到阳性结果,您仍然需要进行调查。

关于c# - 在 C# 中使用反射检测编译器生成的默认构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3190575/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com