What the hell is that?

No ifs, ands, or buts, news about Net Standard may be old and stale, but one thing still applies: As Steve Martin would say, “What the hell is that?” Believe it or not, I started writing this entry years ago, a few months after Net Standard first came out. I had hoped that if I just write about it, I’d somehow stumble across what the hell it is. But, I didn’t get anywhere because most people don’t know what the hell they’re talking about. Fast forward to now.

Due to my work on writing a C# compiler for the GPU called Campy, I’ve made another diversion into Net Standard to try to understand what is going on here. Unfortunately, the folks at Microsoft don’t help. In one place, they say Net Standard is “a formal specification of .NET APIs that are intended to be available on all .NET implementations.” In another place, MS says “The official specification is a set of .cs files that define the APIs that are part of the standard. The ref directory in the dotnet/standard repository defines the .NET Standard APIs.” Another, a well-known developer and ex-professor, wrote “.NET Standard isn’t a runtime. It’s not something you can install. It’s not an ‘instance of .NET.'” If you go to the Net Standard FAQ, there’s a whole lot of stuff, mostly not useful. And, you get a hint about what the hell it is if you try to sit back and watch a long lecture from some guy on type forwarding.

These descriptions at best use imprecise English. The reality is that there is no spec, because as MS admits it’s really just C# code. End of game. When I was growing up many years ago, a specification was a document, not code. If my advisor 30 years ago saw a student handing in code for a spec, then he’d give out an F. Even in a first-year computer science class, you learn that while there may be some different types of specifications, an implementation isn’t one. Why? Specifications should describe what something is supposed to do, what is is supposed to fulfill. A bunch of C# files doesn’t indicate how it’s packaged up, what each type in the API does, what happens if the implementations (Net Core, Net Framework, Mono) differ. The document at  https://raw.githubusercontent.com/dotnet/standard/master/docs/versions/netstandard2.0_ref.md, nor the code at https://github.com/dotnet/standard/tree/master/src/netstandard/ref, are formal specifications, nothing like what we see in a traditional sense, e.g., B [Abr 96], Vienna development method (VDM; [Luc 87]), etc. Some people say it’s just that .md file. But, that brings up the question: Is the .md file the spec, or the C# files? And, what happens if they differ?

Is it a specification?

Microsoft will keep insisting that C# code is a spec. But, there are no semantics for the API described, no description of what each method does, no pre-, post-conditions, nor invariants. For all I know, a method like System.Console.WriteLine() could do anything, even compute the value of π, yet would it still be a framework that conforms to Net Standard? There could be a spec for Net Standard, but the C# ref files should never be called a specification.

What is it? Net Standard is C# code, which employs some cleverness to generate an interchangeable assembly called netstandard.dll for each platform. Each netstandard.dll contains the types for a common subset of across the different platforms.

Type Forwarding

The crux of Net Standard is a compiler feature called type forwarding. Type forwarding allows one to set a link of a type in an “interface assembly” (or what MS calls a “reference assembly”–more details about the term below), to a type in an “implementation assembly”. Type forwarding uses the attribute System.Runtime.CompilerServices.TypeForwardedTo. In addition, using System.Runtime.CompilerServices.TypeForwardedFrom attribute, one can set up a reverse link.

“Type Forwarding To” links invoke a special action with the metasystem. During type resolution, the metasystem will try to find all the types that are used by a given type. A type definition could exist in another assembly, and so it is marked as to where the system can find it. Normally, if you use a type, it just puts in a reference directly to the assembly that contains the typedef. For “Type Forward To”, one can create an indirect typedef in the assembly.

A nice description is at Redgate Hub, here (and a description of PE files here). But, here is a simple example of how it may be used.

Application assembly code

namespace type_forward
{
    class Program
    {
        static void Main(string[] args)
        {
            ClassLibrary1.Foobar.Hello();
        }
    }
}

Interface assembly code

[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(ClassLibrary1.Foobar))]

Implementation assembly code

namespace ClassLibrary1
{
    public class Foobar
    {
        public static void Hello()
        {
            System.Console.WriteLine("Hello World!");
        }
    }
}

The best way to view what it does is to use DotPeek and look at all three assemblies produced. There, you can see in the interface assembly an ExportedType entry for Foobar.

So, what the hell is Net Standard?

Net Standard is in fact really just a standardized assembly, netstandard.dll, built for various frameworks. Because the version information for netstandard.dll is the same, e.g., 2.0.0.0. Each netstandard.dll (e.g., in C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.1.7\netstandard.dll for Net Core 2.1, or C:\Windows\Microsoft.NET\Framework64\v4.0.30319\netstandard.dll for Net Framework 4.7.1) is an interface assembly, which contains links for all the types common across all platforms.

So, for the type Action, the ExportedType entry is:

  • netstandard.dll in Net Framework 4.7.1
    • TypeDefId=0, Name=Action, Namespace=System, Implementation=mscorlib
  • netstandard.dll in Net Core 2.1.7
    • TypeDefId=0, Name=Action, Namespace=System, Implementation=System.Runtime.

Out of sight, out of mind?

 Given that, there are two things that can trip you up. First, it’s possible to use reflection to call methods of a type not available directly through the API. For example, BitConverter.SingleToInt32Bits(float) isn’t in Net Standard 2.0, but I can call the method in Net Core 2.0; System.PlatformID has an attribute for Serializable in Net Framework 4.6.1, but it’s missing in Net Core 2.0; the class System.Runtime.InteropServices.RuntimeEnvironment is marked abstract in Net Core 2.0, but it’s missing in Net Framework 4.6.1.

So, while you may compile something to Net Standard, it doesn’t mean that you can’t access details in the implementation framework.

Conclusion

Net Standard is at best an operationally defined specification, but I would never call it a specification at all because it’s C# code, and because it does not describe the semantics of the type. I would instead define Net Standard as an interchangeable, common interface library to a particular implementation library for the framework (e.g., Net Framework, Net Core, Mono, etc.) via type forwarding

That’s what the hell it is.

 

[Abr 96] Abrial J.-R, The B-Book: Assigning Programs to Meanings, Cambridge University Press, 1996.

[Luc 87] Lucas, Peter. “VDM: Origins, hopes, and achievements.” International Symposium of VDM Europe. Springer, Berlin, Heidelberg, 1987.

 

 

Posted in Tip