为Nullable <t>序列化生成IL?</t>

时间:2015-03-11 16:01:21

标签: c# nullable reflection.emit il conversion-operator

我正在编写自己的序列化程序,它会发出IL来生成[de]序列化代码。

对于nullables,我认为我可以生成以下内容(以int?为例)(假设我们已经生成[de]序列化int的方法):

public static void Serialize(Stream stream, int? value, object context)
{
    Serialize(stream, (int)value, context);
}

public static void Deseiralize(Stream stream, out int? value, object context)
{
    int tmp;
    Deserialize(stream, out tmp, context);
    value = tmp;
}

这是我如何产生的:

    public override void GenSerializationCode(Type type)
    {
        var underlyingType = Nullable.GetUnderlyingType(type);
        var serialize = GetSerializeCall(underlyingType);

        // Serialize(stream, (UnderlyingType)value, context);
        emit.ldarg_0()
            .ldarg_1()
            .unbox_any(underlyingType)
            .ldarg_2()
            .call(serialize)
            .ret();
    }

    public override void GenDeserializationCode(Type type)
    {
        var underlyingType = Nullable.GetUnderlyingType(type);
        var deserialize = GetDeserializeCall(underlyingType);

        // UnderlyingType tmp; Deserialize(stream, out tmp, context);
        var tmp = emit.declocal(underlyingType);
        emit.ldarg_0()
            .ldloca_s(tmp)
            .ldarg_2()
            .call(deserialize);

        // value = tmp;
        emit.ldarg_1()
            .ldloc_s(tmp)
            .stind_ref()
            .ret();
    }

我还生成一个用于调试的程序集。我在ILSpy中加载它,C#代码看起来与我的想法完全一样。但peverify还有其他的话要说......

enter image description here

我想了一会儿,然后意识到Nullable<T>是一个结构,所以我应该使用Ldarga代替Ldarg,所以我将ldarg_1()更改为{ {1}}

现在peverify给出:

ldarga(1)

我认为它与[IL]: Error: [C:\Users\vexe\Desktop\MyExtensionsAndHelpers\Solution\CustomSerializer\bin\Release\SerTest.dll : FastSerializer::Serialize][offset 0x00000007][found address of value 'System.Nullable`1[System.Int32]'] Expected an ObjRef on the stack. 转化运算符有关,所以我尝试了Nullable<T>属性:

Value

peverify很高兴这个!

问题是,为什么在将可为空的nullable转换为其基础类型时,从 var underlyingType = Nullable.GetUnderlyingType(type); var serialize = GetSerializeCall(underlyingType); var getValue = type.GetProperty("Value").GetGetMethod(); // Serialize(stream, value.get_Value(), context); emit.ldarg_0() .ldarga(1) .call(getValue) .ldarg_2() .call(serialize) .ret(); T的显式运算符没有启动?

此外,即使在执行Nullable<T>时使用Ldarga代替Ldarg,我也无法摆脱反序列化中的错误 - 我想我可以尝试一下隐式转换正在做。即value = tmp;,但我想知道我做错了什么。

注意:&#39;发出&#39;只是我用来生成IL的助手。它在内部使用value = new Nullable<int>(tmp);并在每次操作后自行返回,因此我可以将调用链接在一起。

编辑:这里是最终的代码,包括笔记和所有内容。

ILGenerator

2 个答案:

答案 0 :(得分:2)

显式和隐式转换纯粹是C#概念。

IL对可空类型没有任何特殊意识(将它们装入Object s除外);您需要明确使用.Value或致电ctor。

例如,请查看IL generated by the C# compiler

答案 1 :(得分:1)

运算符仅适用于C#和VB.NET编译器。 IL和IL几代人对此一无所知。