gpt4 book ai didi

c# - 断言子类调用的特定构造函数

转载 作者:太空宇宙 更新时间:2023-11-03 21:03:25 26 4
gpt4 key购买 nike

我正在尝试编写一个测试来验证子类是否调用特定基类的构造函数。实际上,我的实体的 ID 是在服务器实例化对象时生成的。我使用 NHibernate 作为我的 ORM,所以我需要一个 0 参数的构造函数。我希望此构造函数不会在每次 NHibernate 合并实体时生成 Guid,因此我使用 Guid 作为参数为我的基本实体创建了第二个构造函数。

看起来像这样

public abstract class EntityBase {
public Guid ID {get; protected set;}

protected EntityBase() { }

public EntityBase(Guid id) { ID = id }

public static Guid NewID => GenerateGuid();
}

public class Entity : EntityBase {
public int X {get; set;}

protected Entity() { }

public Entity(int x) : base(NewID) { X = x; }
}

我想写的这个测试应该断言 EntityBase 的所有具体子类的所有构造函数(0 参数构造函数除外)调用正确的基构造函数:

public EntityBase(Guid id) { ID = id }

目前,我的代码循环遍历所有可从 EntityBase 分配的具体类的构造函数,但我不知道如何进行最终检查。对解决方案的研究建议尝试使用反射来读取 IL。我考虑过尝试检查是否已调用“NewID”,但也无法找到任何完成的方法。

有没有办法实现这一点,或者我对 NHibernate 问题的解决方案才是真正的问题?

最佳答案

好吧,这花了我一段时间,但我喜欢挑战。

开始前的警告:MSDN 上未列出某些 OperandType 值的字节大小,因此我无法确定这是否正确解析了所有内容。

您传入派生构造函数和基础构造函数,然后判断该构造函数是否已被调用。

public bool IsCalled(ConstructorInfo derivedConstructor, ConstructorInfo baseConstructor)
{
var body = derivedConstructor.GetMethodBody();
var expectedConstructorToken = baseConstructor.MetadataToken;
byte[] il = body.GetILAsByteArray();

var codes = new Dictionary<short, OpCode>();
var fields = typeof(OpCodes).GetFields(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
foreach (var field in fields)
{
var value = field.GetValue(null);
if (!(value is OpCode)) { continue; }
var opCode = (OpCode)value;
codes.Add(opCode.Value, opCode);
}

var operandSizes = new Dictionary<OperandType, int>();
// https://msdn.microsoft.com/en-us/library/system.reflection.emit.operandtype(v=vs.110).aspx
operandSizes.Add(OperandType.InlineBrTarget, 4);
operandSizes.Add(OperandType.InlineField, 4);
operandSizes.Add(OperandType.InlineI, 4);
operandSizes.Add(OperandType.InlineI8, 8);
operandSizes.Add(OperandType.InlineMethod, 4);
operandSizes.Add(OperandType.InlineNone, 0);
operandSizes.Add(OperandType.InlineR, 8);
operandSizes.Add(OperandType.InlineSig, 4);
operandSizes.Add(OperandType.InlineString, 4);
operandSizes.Add(OperandType.InlineSwitch, 4);
operandSizes.Add(OperandType.InlineType, 32);
operandSizes.Add(OperandType.InlineVar, 2);
operandSizes.Add(OperandType.ShortInlineBrTarget, 1);
operandSizes.Add(OperandType.ShortInlineI, 1);
operandSizes.Add(OperandType.ShortInlineR, 4);
operandSizes.Add(OperandType.ShortInlineVar, 1);

var i = 0;
while(i < il.Length) {
OpCode operation = OpCodes.Nop;
if (il[i] == 0xfe)
{
operation = codes[BitConverter.ToInt16(il, i)];
}
else
{
operation = codes[(short)il[i]];
}
i += operation.Size;

if (operation.OperandType == OperandType.InlineMethod)
{
var token = BitConverter.ToInt32(il, i);
if (token == expectedConstructorToken) { return true; }
}

i += operandSizes[operation.OperandType];
}
return false;
}

用法:

var derivedConstructor = typeof(Derived).GetConstructor(new Type[] { typeof(int) });
var baseConstructor = typeof(Base).GetConstructor(new Type[] { typeof(int) });
bool isCalled = IsCalled(derivedConstructor, baseConstructor);

关于c# - 断言子类调用的特定构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43083227/

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