2

This code reproduces the problem:

Type Resolver(int fieldNumber)
{
    return typeof(ConsoleColor?);
}

var stream = new MemoryStream();
ConsoleColor? obj = ConsoleColor.Green;
Serializer.NonGeneric.SerializeWithLengthPrefix(stream, obj, PrefixStyle.Base128, 1);

stream.Position = 0;
Serializer.NonGeneric.TryDeserializeWithLengthPrefix(stream, PrefixStyle.Base128, Resolver, out var dd);
//EXCEPTION!!!

I am simply serializing a nullable enum value. But when I try to deserialize, I get below exception:

ProtoBuf.ProtoException: Invalid wire-type; this usually means you have over-written a file without truncating or setting the length; see https://stackoverflow.com/q/2152978/23354'

What am I doing wrong? Is there any way to fix or work around the issue? I am using version 2.4.6 of protobuf-net library. (Cannot move to 3.x because I am stuck with .Net Framework 4.0).

Hemant
  • 19,486
  • 24
  • 91
  • 127

1 Answers1

3

When you pass a Nullable<T> into an API that takes object, it is boxed, and the boxing rules for Nullable<T> are special: it boxes to either a regular null, or a box of T, not a box of T?; in other words: an object value never contains a nullable value-type. Because of this, from the library's perspective you sent a ConsoleColor, not a ConsoleColor?, so:

Type Resolver(int fieldNumber)
{
    return typeof(ConsoleColor);
}

As a side note, though: the resolver API is very specific and niche, and there are usually better ways to do things. If you can tell me what you're trying to achieve, I can probably offer more guidance.

(I'm trying to think whether there is ever a scenario where the resolver could meaningfully return a typeof(Nullable<T>) for some T - we could perhaps have the library just unwrap the nullable itself, or raise a more meaningful message)

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Thank Marc! Yes I do see the issue now. Actually I have a RPC library which automatically generates the serialiser for method arguments. For example, during a call to `int Sum(int a, int b)`, library serialises the arguments `a` (with field number 1), and `b` (with field number 2) and sends it over wire. On the receiving end, it does the reverse to get the arguments and invokes the function call. I got stuck when I used `SomeEnum?` as argument. – Hemant Aug 07 '20 at 15:10
  • But I still don't understand why `int?` works but `SomeEnum?` doesn't? – Hemant Aug 07 '20 at 15:14