简体   繁体   English

如何将方法调用转到自检规则中的Introspector中的Action,Func或Delegate?

[英]How can I walk the method call to an Action, Func, or Delegate in introspector for a custom rule?

I'm writing a custom rule to verify the constructor of any control type calls initialize component. 我正在编写一个自定义规则,以验证任何控件类型的构造函数调用初始化组件。

But when I hit these 2 edge cases: 但是,当我遇到以下两种情况时:

public Form1(int? testInt,bool testBool,bool testBool2)
        : this(false)
    {
        Action init = ( ) => InitializeComponent( );
        init();
    }
    public Form1(int? testInt, bool testBool, bool? testBool2)
        : this(false)
    {
        Action init = InitializeComponent;
        init( );
    }

I can't seem to walk the init call to see that initializeComponent is getting called in these constructors. 我似乎无法通过init调用来看到在这些构造函数中调用了initializeComponent。 I know this is an edge case, and unlikely to happen, but I want to learn how to do it. 我知道这是一个极端的情况,不太可能发生,但是我想学习如何做。

The reflector IL looks like this: 反射器IL如下所示:

.method public hidebysig specialname rtspecialname instance void .ctor(valuetype [mscorlib]System.Nullable`1<int32> testInt, bool testBool, bool testBool2) cil managed
{
.maxstack 3
.locals init (
    [0] class [mscorlib]System.Action init,
    [1] class [mscorlib]System.Action CS$<>9__CachedAnonymousMethodDelegateb)
L_0000: ldnull 
L_0001: stloc.1 
L_0002: ldarg.0 
L_0003: ldc.i4.0 
L_0004: call instance void TestLibrary.Form1::.ctor(bool)
L_0009: nop 
L_000a: nop 
L_000b: ldloc.1 
L_000c: brtrue.s L_001d
L_000e: ldarg.0 
L_000f: ldftn instance void TestLibrary.Form1::<.ctor>b__a()
L_0015: newobj instance void [mscorlib]System.Action::.ctor(object, native int)
L_001a: stloc.1 
L_001b: br.s L_001d
L_001d: ldloc.1 
L_001e: stloc.0 
L_001f: ldloc.0 
L_0020: callvirt instance void [mscorlib]System.Action::Invoke()
L_0025: nop 
L_0026: nop 
L_0027: ret 
}


.method public hidebysig specialname rtspecialname instance void .ctor(valuetype [mscorlib]System.Nullable`1<int32> testInt, bool testBool, valuetype [mscorlib]System.Nullable`1<bool> testBool2) cil managed
{
.maxstack 3
.locals init (
    [0] class [mscorlib]System.Action init)
L_0000: ldarg.0 
L_0001: ldc.i4.0 
L_0002: call instance void TestLibrary.Form1::.ctor(bool)
L_0007: nop 
L_0008: nop 
L_0009: ldarg.0 
L_000a: ldftn instance void TestLibrary.Form1::InitializeComponent()
L_0010: newobj instance void [mscorlib]System.Action::.ctor(object, native int)
L_0015: stloc.0 
L_0016: ldloc.0 
L_0017: callvirt instance void [mscorlib]System.Action::Invoke()
L_001c: nop 
L_001d: nop 
L_001e: ret 
}

I'm walking the constructors as such: 我正在这样的构造函数:

        private Dictionary<Method, bool> _methodContainsInitCall;
public void VisitConstructorsRecursive(Method method, Method initializer)
    {
        Debug.Assert(_methodContainsInitCall.ContainsKey(method));
        var toVisit = new List<Method>( );
        foreach (var instruction in method.Instructions.Where(x => x.OpCode==OpCode.Call || x.OpCode== OpCode.Callvirt))
        {

            if (instruction.Value is Method)
            {
                //&&((Method)instruction.Value).FullName.Contains(".#ctor")
                var callMethod =(Method)instruction.Value;
                if (callMethod.FullName.StartsWith("System.Windows.Forms.Form.#ctor"))
                    continue;
                if (callMethod.IsStatic==false) //can not call instance method InitializeComponent in static method
                {
                    toVisit.Add(callMethod);
                }

                //
                //TestLibrary.Form1.#ctor(System.String)
            }
            if (instruction.Value is Method&&((Method)instruction.Value).FullName.EndsWith(".InitializeComponent"))
            {
                if (_constructorFoundInitializeCall.ContainsKey(method))
                    _constructorFoundInitializeCall[method]=true;
                _methodContainsInitCall[method]=true;
                return;
            }

        }
        foreach (var methodCall in toVisit)
        {
            if (_methodContainsInitCall.ContainsKey(methodCall))
            {
                if (_methodContainsInitCall[methodCall])
                {
                    _constructorFoundInitializeCall[initializer]=true;
                    return;
                }
            }
            else
            {
                _methodContainsInitCall.Add(methodCall, false);
                VisitConstructorsRecursive(methodCall, initializer);
            }


        }
    }

when the virtual call to Action.Invoke() happens, it is flagged as virtual and method.Instructions.Count ==0 as well as method.Body.Count==0 当对Action.Invoke()的虚拟调用发生时,它被标记为virtual和method.Instructions.Count ==0以及method.Body.Count==0

so where is my instruction to call the initialize component hiding that I can verify it is actually being called? 所以我调用初始化组件的指令隐藏在哪里,我可以验证它实际上在被调用?

When walking the blocks that make up the constructor, notice the CS$<>9__CachedAnonymousMethodDelegateb typed variable. 当遍历组成构造函数的块时,请注意CS $ <> 9__CachedAnonymousMethodDelegateb类型变量。 That class actually contains the compiled delegate that calls InitializeComponent. 该类实际上包含调用InitializeComponent的已编译委托。

If you want to check this, check the locals of the method, find visit the compiler generated type and you will find that it only has one method. 如果要检查这一点,请检查该方法的本地方法,找到访问编译器生成的类型,然后您将发现它只有一个方法。 Visit that method to check whether it calls Initialize Component. 访问该方法以检查它是否调用了初始化组件。

The problem is that the only way to detect these types of classes is by name. 问题在于,检测这些类型的类的唯一方法是按名称。 And the C# and VB.NET compilers use a different naming scheme to store these anonymous delegates. C#和VB.NET编译器使用不同的命名方案来存储这些匿名委托。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM