How to extend string to deserialize binary data

306 views Asked by At

I'm using my own extension method to serialize strings (and more data types) into a file with custom binary format (external, I cannot modify that format). My method is:

public static byte[] Serialize(this string str)
{
    if (str.Length > short.MaxValue)
        throw new ArgumentOutOfRangeException("str", "Max length allowed is " + short.MaxValue.ToString());
    List<byte> data = new List<byte>();
    data.Add(0);
    data.Add(0);
    if (str != null)
    {
        byte[] buffer = Encoding.UTF8.GetBytes(str);
        data.AddRange(buffer);
        data[0] = (byte)(buffer.Length % 256);
        data[1] = (byte)((buffer.Length / 256) >> 8);
    }
    return data.ToArray();
}

An example of usage:

string str1 = "Binary String";
byte[] data = str1.Serialize();

Result is

data = { 13, 0, 66, 105, 110, 97, 114, 121, 32, 83, 116, 114, 105, 110, 103 }

Now I'm trying to add another extension method to deserialize when reading from these files:

public static void Deserialize(this string str, byte[] data)
{
    if (data == null || data.Length < 2)
    {
        str = null;
    }
    else
    {
        short length = (short)(data[0] + (data[1] << 8));
        if (data.Length != length + 2)
            throw new ArgumentException("Invalid data", "data");
        str = Encoding.UTF8.GetString(data, 2, length);
    }
}

If I try this:

string str2 = null;
str2.Deserialize(data);

The expected result for str2 is

"Binary String"

The actual result is

null

However, when debugging step by step, str inside Deserialize() gets the correct value at line str = Encoding.UTF8.GetString(data, 2, length);.

Also tried this:

string str3 = string.Deserialize(data);

But it does not compile, and the error message is

Error 1 'string' does not contain a definition for 'Deserialize'

I don't know what I'm doing wrong. Any idea on how to solve it?

2

There are 2 answers

0
DavidG On BEST ANSWER

The first parameter of an extension method is the object you are acting on. In this case it is the byte array data. The return type is what you are wanting to stuff into the string variable. Therefore, the signature for the Deserialize method should be:

public static string Deserialize(this byte[] data)

Also inside the method you need to return the string value so your full method should be (note that I simplified it very slightly):

public static string Deserialize(this byte[] data)
{
    if (data == null || data.Length < 2)
        return null;

    short length = (short)(data[0] + (data[1] << 8));
    if (data.Length != length + 2)
        throw new ArgumentException("Invalid data", "data");

    return Encoding.UTF8.GetString(data, 2, length);
}

And you use it like this:

string str1 = "Binary String";
byte[] data = str1.Serialize();
string str2 = data.Deserialize();
2
Alex Sikilinda On

Although DavidG's variant is 100% correct, I will add some more explanation. Firs of all, when you invoke extension method on an object, actually a static method will be invoke instead, so

str2.Deserialize(data);

will be transformed to

ClassThatContainsExtensionMethod.Deserialize(str2, data);

Now let's remember that C# passes parameters by value, that means that another reference will be created.

In your Deserialize() method you modify the new string, not the older one (because it's a copy), when you do

str = Encoding.UTF8.GetString(data, 2, length);

you actually change this local str, while the original one stays the same (i.e. null).

FYK, you can pass methods parameters by reference, using ref and out keywords.

BTW, are you really sure, that two bytes for length is enough?