首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >分离C#构造函数的前后基或链式构造函数调用说明

分离C#构造函数的前后基或链式构造函数调用说明
EN

Stack Overflow用户
提问于 2014-10-30 02:40:55
回答 1查看 171关注 0票数 3

对于使用代码编织向C#添加混合器的项目,我将从源mixin类型的无参数实例构造函数克隆代码到目标类型中的构造函数。为此,我将构造函数划分为三个概念部分,这就是我所寻求的帮助。

以下是三部分:

  1. 在基或链式构造函数调用之前运行的字段初始化。
  2. 基或链式构造函数调用,包括将参数加载到堆栈。
  3. 根据在构造函数体中编写的源代码编译的实际构造函数代码。

其基本思想是将源构造函数复用到这些片段中。复用步骤还包括检查局部变量(stloc*和ldloc*),因此指令分离是正确的非常重要。调用基本构造函数的目标构造函数是代码克隆目标。每个方法都将将源的第1节克隆到其第1节中,并在其第3节中添加一个方法调用,该调用将调用一个新方法,该方法将在目标类型中包含源构造函数的第3节代码。(它被放入自己的方法中,主要是因为有可能出现多个退出点。)

我已经阅读过C#规范的实例构造函数部分,但是除了确认我正在看到的三个部分的有意存在之外,我认为它没有帮助。在这件事上,我有几个很有希望的错误开始,而不是尝试另一个坏策略,它通过了我的测试用例,然后一旦碰到我没有想到的事情就会窒息,我希望我能从有更好经验的人那里得到一些更好的输入。

我当前的“下一步”思想是循环执行查找ldarg.0的指令,然后检测下一个方法调用。如果下一个方法调用是一个基构造函数或链式构造函数,那么我可以调用这个第2节,在第1节之前使用指令,在第3节之后调用指令。不过,我担心指令可能并不总是有如此清晰的分离,我不知道如何才能确定这样的事情。

另一个想法是,由于规范明确规定变量初始化指令位于基或链式调用构造函数之前,因此查找设置本地字段的指令的结尾可能更可靠。不幸的是,我不知道怎样才是最好的方法。

下面是一个目标类型的例子,以及我正在寻找的构造函数的概念分解。

代码语言:javascript
复制
public class MultipleConstructorsTarget : MultipleConstructorsTargetBase
{
    public MultipleConstructorsTarget()
    {
        var values = Tuple.Create(783535, "KNion wineofn oianweiof nqiognui ndf", new UriBuilder { Host = "j.k.l" });

        this.OriginalUninitializedInt = values.Item1;
        this.OriginalUninitializedString = values.Item2;
        this.OriginalUninitializedObject =  values.Item3;
    }

    public MultipleConstructorsTarget(int i) : this(i, "A iuohiogfniouhe uihui iu.", new UriBuilder { Host = "g.h.i" }) { }

    public MultipleConstructorsTarget(int i, string j) : this(i, j, new UriBuilder { Host = "d.e.f" }) { }

    public MultipleConstructorsTarget(int i, string j, UriBuilder k)
        : base(i)
    {
        this.OriginalUninitializedInt = i;
        this.OriginalUninitializedString = j;
        this.OriginalUninitializedObject = k;
    }

    public int OriginalInitializedInt = 48685;
    public string OriginalInitializedString = "Tion3lao ehiuawh iuh buib ld";
    public UriBuilder OriginalInitializedObject = new UriBuilder { Host = "a.b.c" };

    public int OriginalUninitializedInt;
    public string OriginalUninitializedString;
    public UriBuilder OriginalUninitializedObject;
}

对于MultipleConstructorsTarget()

第1节

代码语言:javascript
复制
  IL_0000:  ldarg.0
  IL_0001:  ldc.i4     0xbe2d
  IL_0006:  stfld      int32 Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalInitializedInt
  IL_000b:  ldarg.0
  IL_000c:  ldstr      "Tion3lao ehiuawh iuh buib ld"
  IL_0011:  stfld      string Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalInitializedString
  IL_0016:  ldarg.0
  IL_0017:  newobj     instance void [System]System.UriBuilder::.ctor()
  IL_001c:  stloc.2
  IL_001d:  ldloc.2
  IL_001e:  ldstr      "a.b.c"
  IL_0023:  callvirt   instance void [System]System.UriBuilder::set_Host(string)
  IL_0028:  ldloc.2
  IL_0029:  stfld      class [System]System.UriBuilder Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalInitializedObject

第2节

代码语言:javascript
复制
  IL_002e:  ldarg.0
  IL_002f:  call       instance void Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTargetBase::.ctor()

第3节

代码语言:javascript
复制
  IL_0034:  ldc.i4     0xbf4af
  IL_0039:  ldstr      "KNion wineofn oianweiof nqiognui ndf"
  IL_003e:  newobj     instance void [System]System.UriBuilder::.ctor()
  IL_0043:  stloc.1
  IL_0044:  ldloc.1
  IL_0045:  ldstr      "j.k.l"
  IL_004a:  callvirt   instance void [System]System.UriBuilder::set_Host(string)
  IL_004f:  ldloc.1
  IL_0050:  call       class [mscorlib]System.Tuple`3<!!0,!!1,!!2> [mscorlib]System.Tuple::Create<int32,string,class [System]System.UriBuilder>(!!0, !!1, !!2)
  IL_0055:  stloc.0
  IL_0056:  ldarg.0
  IL_0057:  ldloc.0
  IL_0058:  callvirt   instance !0 class [mscorlib]System.Tuple`3<int32,string,class [System]System.UriBuilder>::get_Item1()
  IL_005d:  stfld      int32 Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalUninitializedInt
  IL_0062:  ldarg.0
  IL_0063:  ldloc.0
  IL_0064:  callvirt   instance !1 class [mscorlib]System.Tuple`3<int32,string,class [System]System.UriBuilder>::get_Item2()
  IL_0069:  stfld      string Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalUninitializedString
  IL_006e:  ldarg.0
  IL_006f:  ldloc.0
  IL_0070:  callvirt   instance !2 class [mscorlib]System.Tuple`3<int32,string,class [System]System.UriBuilder>::get_Item3()
  IL_0075:  stfld      class [System]System.UriBuilder Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalUninitializedObject
  IL_007a:  ret

对于MultipleConstructorsTarget(int i)

第1节

(空)

第2节

代码语言:javascript
复制
  IL_0000:  ldarg.0
  IL_0001:  ldarg.1
  IL_0002:  ldstr      "A iuohiogfniouhe uihui iu."
  IL_0007:  newobj     instance void [System]System.UriBuilder::.ctor()
  IL_000c:  stloc.0
  IL_000d:  ldloc.0
  IL_000e:  ldstr      "g.h.i"
  IL_0013:  callvirt   instance void [System]System.UriBuilder::set_Host(string)
  IL_0018:  ldloc.0
  IL_0019:  call       instance void Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::.ctor(int32, string, class [System]System.UriBuilder)

第3节

代码语言:javascript
复制
  IL_001e:  ret

对于MultipleConstructorsTarget(int i, string j)

第1节

(空)

第2节

代码语言:javascript
复制
  IL_0000:  ldarg.0
  IL_0001:  ldarg.1
  IL_0002:  ldarg.2
  IL_0003:  newobj     instance void [System]System.UriBuilder::.ctor()
  IL_0008:  stloc.0
  IL_0009:  ldloc.0
  IL_000a:  ldstr      "d.e.f"
  IL_000f:  callvirt   instance void [System]System.UriBuilder::set_Host(string)
  IL_0014:  ldloc.0
  IL_0015:  call       instance void Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::.ctor(int32, string, class [System]System.UriBuilder)

第3节

代码语言:javascript
复制
  IL_001a:  ret

对于MultipleConstructorsTarget(int i, string j, UriBuilder k)

第1节

代码语言:javascript
复制
  IL_0000:  ldarg.0
  IL_0001:  ldc.i4     0xbe2d
  IL_0006:  stfld      int32 Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalInitializedInt
  IL_000b:  ldarg.0
  IL_000c:  ldstr      "Tion3lao ehiuawh iuh buib ld"
  IL_0011:  stfld      string Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalInitializedString
  IL_0016:  ldarg.0
  IL_0017:  newobj     instance void [System]System.UriBuilder::.ctor()
  IL_001c:  stloc.0
  IL_001d:  ldloc.0
  IL_001e:  ldstr      "a.b.c"
  IL_0023:  callvirt   instance void [System]System.UriBuilder::set_Host(string)
  IL_0028:  ldloc.0
  IL_0029:  stfld      class [System]System.UriBuilder Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalInitializedObject

第2节

代码语言:javascript
复制
  IL_002e:  ldarg.0
  IL_002f:  ldarg.1
  IL_0030:  call       instance void Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTargetBase::.ctor(int32)

第3节

代码语言:javascript
复制
  IL_0035:  ldarg.0
  IL_0036:  ldarg.1
  IL_0037:  stfld      int32 Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalUninitializedInt
  IL_003c:  ldarg.0
  IL_003d:  ldarg.2
  IL_003e:  stfld      string Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalUninitializedString
  IL_0043:  ldarg.0
  IL_0044:  ldarg.3
  IL_0045:  stfld      class [System]System.UriBuilder Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalUninitializedObject
  IL_004a:  ret

我正在使用Mono.Cecil进行我所有的IL读写。如果您感兴趣,可以在Bix.Mixers上找到https://github.com/rileywhite/Bix.Mixers.Fody的项目代码。这个问题涉及的特定文件位于https://github.com/rileywhite/Bix.Mixers.Fody/blob/master/src/Bix.Mixers/Fody/ILCloning/ConstructorMultiplexer.cs

EN

回答 1

Stack Overflow用户

发布于 2014-11-01 19:58:23

一种似乎有效的策略是通过构造函数指令枚举,将它们分组如下:

  1. 如果一个指令不是ldarg.0,那么它本身就被放入一个组中。
  2. 如果一条指令是ldarg.0,那么所有下面的指令都会与它一起分组,直到满足以下条件之一:
    • 遇到了调用、virtcall或calli指令。
    • 指令是另一个ldarg.0之前的最后一个指令。
    • 所有指示都已列举。

一旦确定了一个组,就会检查组中的最后一个指令。如果它是一个调用指令,并且操作数是一个基构造函数或链式构造函数,那么这个组被标识为第2节,这意味着前面的指令是第1节,其余的指令是第3节。

下面的代码,给定要开始查看的索引,根据这些规则标识一组指令。

代码语言:javascript
复制
public static bool TryGetNext(IList<Instruction> sourceInstructions, int firstIndex, out InstructionGroup instructionGroup)
{
    Contract.Requires(sourceInstructions != null);

    if (firstIndex < 0 || firstIndex >= sourceInstructions.Count)
    {
        instructionGroup = null;
        return false;
    }

    var instructions = new List<Instruction>();
    var instruction = sourceInstructions[firstIndex];
    instructions.Add(instruction);


    int lastIndex;
    if (instruction.OpCode.Code != Code.Ldarg_0) { lastIndex = firstIndex; }
    else
    {
        int i;
        // calls into base and chained constructors start like this
        // so we'll look for the next call instruction or any instruction where the next instruction is another ldarg.0
        // meaning that the stack was cleared at some point
        // there is no assumption that this grouping is generally useful, but the hope is that it will catch constructor calls in this specific case
        var isLastInstructionFound = false;
        for (i = firstIndex + 1; !isLastInstructionFound && i < sourceInstructions.Count; i++)
        {
            instruction = sourceInstructions[i];
            instructions.Add(instruction);
            if (instruction.OpCode.Code == Code.Call ||
                instruction.OpCode.Code == Code.Callvirt ||
                instruction.OpCode.Code == Code.Calli ||
                (instruction.Next != null && instruction.Next.OpCode.Code == Code.Ldarg_0))
            {
                isLastInstructionFound = true;
            }
        }

        lastIndex = i - 1;
    }

    instructionGroup = new InstructionGroup(firstIndex, lastIndex, instructions);
    return true;
}

如果您感兴趣,可以在https://github.com/rileywhite/Bix.Mixers.Fody/blob/0.1.7/src/Bix.Mixers/Fody/ILCloning/ConstructorMultiplexer.cs上看到完整的代码。

尽管这似乎是可行的,但我不会选择这个作为最终答案,因为这仍然只是另一个启发。我很想从更有经验的人那里得到一个真正的答案。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26644148

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档