/ .NET

Handling the Serializable Attribute in .NET Core

Now that I can have FTx Server Library as part of my Visual Studio Code 'solution', and I've pulled in the very simple class LiteralOccurrence, I should be able to get back on track for porting the BTree class, right? I mean, come-on -- LiteralOccurrence isn't much more than a Pair or 2-Tuple type of class. Heck, it probably could be a struct.

But, alas, no. We are blocked again. Here's the whole class:

[Serializable]
public sealed class LiteralOccurrence
{
	public LiteralOccurrence(string literal, long occurrence)
	{
		Literal = literal;
		Occurrence = occurrence;
	}

	public string Literal;
	public long Occurrence;
}

SerializableAttribute, why do you hate us so? We cannot compile this class because of this attribute.

Why?

Because binary serialization is not part of .NET Core 1.0 (although there is work on BinaryFormatter and hopefully this will make it into a not-too-distant release).

Part of the exercise of porting this code is to also modernize it. So it's worth asking -- why were any classes marked Serializable when this code was first written? Often, it was in support of .NET Remoting, and any aspect of .NET Remoting is likely a strong candidate for a rewrite, anyway.

So, what do we do? Do we blindly search and remove all instances of [Serializable]? Well, we could. But there's an even easier answer. First, remind yourself that, in general, attributes do not actually do anything. They are meta data which is used by something else. When you compile this class under .NET Framework, the class has SerializedAttribute 'attached' to it, but, otherwise, there is no executable code which is run. It's not until you make use of some code which is looking for these attributes that they have any meaning. In this case, when you make use of an IFormatter such as BinaryFormatter, it uses the attribute metadata to determine how and what to serialize.

Therefore, rather than finding and removing all of these attributes, we can use a .NET "polyfill." A polyfill is generally considered to be a piece of code which provides a 'missing' piece of technology, typically in an older version of something. For example, javascript-based polyfills are popular in web pages, mimicking the behavior of a new HTML5 feature in an older, non-HTML5 browser. In this case, we can just create our own, really-does-not-do-anything implementation of System.SerializableAttribute. Some folks may prefer that it be called a stub or a shim rather than a polyfill; they can debate that elsewhere.

Another advantage of this approach versus removing all of the instances of [Serializable] is that if Serializable makes it into a future .NET Core, we don't have to put all of the attributes back -- we just have to remove the polyfill.

Since we now can easily add additional projects and project references to our 'solution', let's create a Bobo.Polyfills class library and add a `System.Serializable' polyfill.

Bobo.Polyfills

And as for the implementation itself, nothing earth-shattering:

namespace System
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Delegate)]
    public class SerializableAttribute : Attribute
    {
    }
}

Lastly, add some references to project.json, and try to build:

build with polyfill

So, as you can see, we've got two (very small) class libraries successfully compiling, and our porting focus can return to BTree.cs. More importantly, we've got some great techniques to reuse as part of the migration process.