1

[de]serialization コードを生成するために IL を発行する独自のシリアライザーを作成しています。

nullables の場合、次を生成できると思いint?ました (ex として取得) ( [de]serialize のメソッドを既に生成していると仮定します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には他に言いたいことがありました...

ここに画像の説明を入力

しばらく考えた後、それが構造体であることに気付いたので、代わりにNullable<T>使用する必要があるので、に変更しましたLdargaLdargldarg_1()ldarga(1)

これで peverify は次のようになります:

[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プロパティを試しました:

        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();

peverify はこれに満足しています。

問題は、 nullable を基になる型にキャストするときに、明示的な演算子 from が以前に開始されTなかったのはなぜですか?Nullable<T>

Ldargaまた、実行するときではなく使用するLdargときでも、Deserialize のエラーを取り除くことができませんでしたvalue = tmp;。暗黙の変換が何をしているのかを試すことができたと思います。つまりvalue = new Nullable<int>(tmp);、何が間違っていたのかを知りたいのです。

注: 「emit」は、IL を生成するために使用する単なるヘルパーです。内部でを使用し、ILGenerator各操作の後にそれ自体を返すので、呼び出しを連鎖させることができます。

編集:これは、メモとすべてを含む、機能した最終的なコードです。

    // Note:
    // 1- IL doesn't know anything about implicit/explicit operators
    //    so we can't make use of the T to Nullable<T> nor Nullable<T> to T operators
    //    that's why we have to use the Value property when serializing and the ctor when deserializing
    // 2- Nullable<T> is a struct
    //    so we use ldarga when calling the property getter when serializing (the property getter is an instance method, so the first argument is always the 'this', but since we're dealing with structs we have to pass 'this' by ref hence ldarga)
    //    then use stobj opcode when constructing an instance when deserializing

    public override void GenSerializationCode(Type type)
    {
        var underlyingType = Nullable.GetUnderlyingType(type);
        var serialize = ctx.GetSerializeCall(underlyingType);
        var getValue = type.GetProperty("Value").GetGetMethod();

        // Serialize(stream, value.get_Value(), ctx);
        emit.ldarg_0()
            .ldarga(1)
            .call(getValue)
            .ldarg_2()
            .call(serialize)
            .ret();
    }

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

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

        // value = new Nullable<UnderlyingType>(tmp);
        var ctor = type.GetConstructor(new Type[] { underlyingType });
        emit.ldarg_1()
            .ldloc_s(tmp)
            .newobj(ctor)
            .stobj(type)
            .ret();
    }
}
4

2 に答える 2

2

Explicit and implicit conversions are a purely C# concept.

IL does not have any special awareness of nullable types (except for boxing them into Objects); you need to explicitly use .Value or call the ctor.

For examples, look at the IL generated by the C# compiler.

于 2015-03-11T16:07:26.950 に答える
1

演算子は、C# および VB.NET コンパイラ専用です。ILとILの世代はそれについて何も知りません。

于 2015-03-11T16:07:57.230 に答える