Tuesday, December 17, 2019

C# 8 Interfaces: Static Main -- Why Not?

When taking a closer look at C# 8 interfaces, we come across static members. Last time we looked at using static fields and methods in an interface. But things get stranger. An interface can have a "static Main" method, which means that this is a valid application:


Okay, so there's technically more to it than this, but still: Yikes!
A static Main method lets us put the entry point to an application in an interface.
Let's take a look at this example to see what it means.

The code for this article is available on GitHub: jeremybytes/interfaces-in-csharp-8. Specifically, this uses the "StaticMain" project.

Note: this article uses C# 8 features which are not available in .NET Framework 4.8. For these samples, I used Visual Studio 16.4.1 and .NET Core 3.1.100.

Static Main
The static Main method is the entry point to an application. In a C# console application, this is generally in the "Program" class. And if you create a new console application using the .NET Core template, you get the following code:


Personal Note: One really sad thing about this template is that it fills in the "Hello World!" for you. So this takes all the fun out of building your first application.

The same is true of ASP.NET Core web applications and APIs. Here's the "Program" class that was created using the "webapi" template:


There is a bit more to the "Program" class than what is seen here, but the "Main" method is the entry point. When we start up the application, it runs "Main".

Static Main in an Interface
With C# 8 we can have static members in an interface. We can also create a "static Main" method in an interface.

Here is an interface that has a static Main method: "IHelloWorld" (from the IHelloWorld.cs file in the StaticMain project on GitHub):


The "Main" method has the same signature as the prior samples from a console application and an API application.

But instead of this method being in the Program class, it is in an interface.

The Shortest Program Class
When we have an interface with a static Main method, it leads to the world's shortest Program class (from the Program.cs file):


This is a valid C# console application (and it actually does something).

Running the Application
Let's see it work. From the command line (open at the project folder), we can type "dotnet run" to run the application.


And if we pass in a command line argument, we see that also works.


Note: When running a console application (or other application) with "dotnet run", we can pass arguments through by adding 2 dashes (--). Everything after those 2 dashes will show up in the "args" parameter on the "Main" method.

So we have a working application that has an empty body for the "Program" class.

"Program" is not Needed
So the "Program" class is empty, and it isn't doing any thing. Do we need it?

No, we don't.

If we remove the "Program" class entirely, the console application still works. That is because the "static Main" method is the entry point to the application; the "Program" class is not the entry point. The "Program" class is used as a convention, but we can use a different class (or interface) as the entry point to the application.
Developers expect to see the entry point inside the Program class. 
Keep that in mind if you are considering something different.

Multiple Entry Points
If we create another interface that has a "static Main" method, we get a compiler error. Here's the message:


"Program has more than one entry point defined. Compile with /main to specify the type that contains the entry point."

This restriction is not new; we've always had this if we tried to define multiple entry points. The difference is that it can apply to interfaces now, too.

This also means that if we have a "static Main" in the Program class, we cannot also have one in an interface (unless we use the compiler switch mentioned in the error message, and that's beyond the scope of this article).

Sure. Why Not?
As mentioned in previous articles (Static Members and Public, Private, and Protected Members), I'm still processing these changes and how they should influence my code.

Right now, I'm still holding onto the idea that interfaces are abstractions. But when we're talking about static members, we really lose that. So if we're talking about putting the entry point of an application into an interface... Why not?

Unfortunately, that is the design decision that came from Microsoft. If you check the Language Design Notes from Oct 17, 2018 (https://github.com/dotnet/csharplang/blob/master/meetings/2018/LDM-2018-10-17.md#main-in-an-interface), you'll find this gem:

"Main in an interface? No reason why not."

Translation: "No point in steering now."

Just Because You Can...
Just because you can do something doesn't mean that you should do something. I'm pretty hard-pressed to come up with a scenario where the entry point of an application would need to come from an interface. (I'll just brace for everyone supplying me with edge-case examples.)

The deeper I get into the changes to interfaces, the more concerned I get. I think that we've lost the meaning for what an interface is.

But there is nothing that forces us to use these features. So if we make the decision, we can continue to use interfaces as abstractions in our code. Even if we do this, it's important to understand what is possible because we may end up using libraries that *do* use these new features. I'm still hoping for the best at this point.

Happy Coding!

Sunday, December 15, 2019

C# 8 Interfaces: Static Members

When taking a closer look at C# 8 interfaces, we have a new capability: interfaces can have static members (including static fields).
Static members give us the ability to run code from an interface without an instance of that interface.
This functionality is similar to how static members work in classes. Let's take a look at an example to show what we can and can't do with static interface members.

The code for this article is available on GitHub: jeremybytes/interfaces-in-csharp-8.

Note: this article uses C# 8 features which are not available in .NET Framework 4.8. For these samples, I used Visual Studio 16.4.1 and .NET Core 3.1.100.

Statics
When we create a static member on a class, we can use that method without creating an instance of that class.

Here's an example that takes the square root of a number and then outputs it to the console along with the current time:


This "Sqrt" (square root) method is a static method on the Math class. So we can take the square root of a number without creating an instance of the Math class.

The same is true of "Console.Writeline" and "DateTime.Now" (although DateTime is a struct rather than a class).

We won't get into whether using "static" is a good idea or not. Static members cause particular difficulty when unit testing. For a look at a workaround that is necessary when using "DateTime.Now" take a look at this article: "Mocking Current Time with a Simple Time Provider".

Today we will look at the mechanics of what is possible by using static members.

An Interface with Static Members
The code for this sample is in the StaticMembers project on GitHub. For this sample, we have a static factory that will return us a data reader.

Here is a summary of the code for the interface (from the IReaderFactory.cs file in the StaticMembers project):


We'll look at the details of what this does in a moment. For now, we'll note that  our interface has 3 members: a private static field, a public static field, and a public static method.
Static Methods on an interface *must* have an implementation. 
Static Fields do not need to be initialized by default (but it's probably a good idea).
Just like with static members on classes, we can call these members without an instance:


The first line sets the "readerType" field, and the second line calls the "GetReader" method. These all happen without having an implementation/instance of the "IReaderFactory" interface.

We'll dig into the details of this in a bit. Let's take a look at the application at large and then dive into the interface members and calling code.

Project Overview
The project is a console application. Here is the project in the solution explorer:


The "DataReaders" folder has the code for the data readers that the factory will create.

The "Factories" folder has the interface that we saw above (as well as a class-based implementation).

The "People.txt" file contains data that is used by one of the readers, and "Program.cs" is the console application itself.

As with most samples, this code is highly simplified. This factory-based approach is a bit complex for a single-project console application. I'd be more likely to use something like this if I had multiple projects and needed to do some type of dynamic loading of dependencies. But we'll take this code "as is" to look at the technical capabilities.

Let's start by touring the data readers.

Data Reader Interface and Implementation
In the "DataReaders" folder, we have an "IPeopleReader" interface. Here's the code for that (from the IPeopleReader.cs file):


This interface has 2 methods. The first gets a collection of "Person" objects, and the second gets an single "Person" based on an identifier. The "Person" class is defined in the "Person.cs" file. It contains a collection of read-write properties and an override of the ToString method. You can check the file in the GitHub project if you'd like details.

We have 2 implementations of this interface. The first is "HardCodedPeopleReader" (from the HardCodedPeopleReader.cs file):


This class implements the "IPeopleReader" interface, so it has the 2 methods. The "GetPeople" method returns a hard-coded list of Person objects. You can check the file on GitHub if you'd like the details.

The second implementation is "CSVPeopleReader" (from the CSVPeopleReader.cs file):


This class also implements "IPeopleReader". But it gets data from a text file in comma-separate values (CSV) format. (This is the "People.txt" file at the root of the project.)

The code is a bit more complex since it is loading and parsing data from the file system. Again, you can check out the GitHub project if you're curious about specific implementation.

The Factory Interface
So let's go back to the factory interface: "IReaderFactory". Here is the detail code (from the IReaderFactory.cs file):


The overall idea of this factory is that "GetReader" will give us a data reader instance based on the "readerType" field. The "savedReader" field will hold the instance so that the data reader is not re-created each time.

The "readerType" public static field contains the type for the data reader we want to use. It is defaulted to the "HardCodedPeopleReader" that we saw above.

Initialization and Implemetation
As noted above, the static fields do *not* need to be initialized. Here we have initialized one but not the other. However, the static method *must* have an implementation.

These requirements make sense if we think about them. A public static field can be set from outside of the interface code (we'll see exactly that below). A private static field can be set from somewhere inside the interface.

But a static method cannot be implemented from outside the interface since it is part of the interface itself. So static methods must have an implementation supplied when they are declared in the interface.

Note: it looks like it's possible to have an external implementation, but we won't get into that here; "extern" has its own set of concerns.

The GetReader Method
Let's walk through the "GetReader" method to see how it works.


First, the "if" statement will check to see if the value stored in the "savedReader" private static field has the same type as the "readerType" public static field. If it matches, then the method will return what is in the saved data reader field.


If the "savedReader" value does not match (or is empty), then then a little bit of reflection is used ("Activator.CreateInstance") to create an instance of the type from the "readerType" static field.

If "CreateInstance" is not able to create an instance (for example, if the constructor requires a parameter), then this will throw an exception that we let bubble up.


After the instance is created, we cast it to "IPeopleReader" using the "as" operator. If the cast is unsuccessful, this does not throw an exception. Instead, it returns null.

The result is assigned to the "savedReader" private static field. This will either have a valid "IPeopleReader" or a "null".


Next we do a null check. If the "savedReader" field is null, then we throw an InvalidOperationException that provides the type of the reader and notes that it does not implement the correct interface.


If we get to the last line of the method, then the "savedReader" private static field should have a valid value. So the last step is to return that value.

This gives us a method that will either return a valid "IPeopleReader" instance or throw an exception.

Calling Static Members
Calling static members on an interface works just like calling static members of a class. The code in the "Program.cs" file is a console application that uses the factory.

Here is the "DisplayPeople" method from the Program.cs file:


The first line uses the static "GetPeople" method on the "IReaderFactory" interface. Since this is a static member, we do not need an instance of an implementation of the interface.

The rest of the method uses the data reader that is returned from the static method: (1) printing the type to the console, (2) calling "GetPeople" on the data reader, (3) displaying the resulting collection, (4) printing any exceptions to the console.

The "DisplayPeople" method is used in the "Main" method of the program (also from the Program.cs file):


The first section calls "DisplayPeople" and will use the default data reader (i.e., "HardCodedPeopleReader"). Here are the results from that first section:


The next section sets the "readerType" public static field to use "CSVPeopleReader". Note that since "readerType" is a "Type", we need to use "typeof()" to get the proper value for the field.

After setting the "readerType", calling "DisplayPeople" again gives us different results:


This data comes from the "People.txt" file and has an extra record ("Jeremy Awesome").

The last section sets the "readerType" field to an invalid value ("Person"). This will trigger the exception handling code:


A Note about Static Fields
This interface has 2 static fields, one public and one private. This is a significant change to interfaces. In C# 7 (and before), interfaces could only contain properties, methods, events, and indexers -- fields were *not* allowed.

The reason for the exclusion of fields is that they hold data that belongs to a particular instance. Something similar could be said for constructors and destructors -- these are implementation details that do not belong to the abstraction.

But *static* fields are different. A static field does *not* belong to an instance. This is not instance-related data; this is data that belongs to the interface itself. This also means that there is only one value for a field. If we have 2 classes that implement an interface with a static field, both classes share that value (more accurately, they share access to a single value).

If the static field is public and assignable, that means that anything can change that value -- including implementing classes or code that has nothing to do with implementation (like in our console application).

Because of the possibility of assignment from multiple sources, we need to be very careful about using public static fields. The value could be changed out from under us at any time. This would lead to unexpected behavior.

Static Fields as Parameters
This sample shows how a static field can be used as a parameter for other members of the interface. These parameters do not need to be restricted to static methods; instance methods can use them as well.

Static fields can be used to parameterize default implementations for non-static members. The Microsoft docs site has a tutorial that shows this: https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/default-interface-methods-versions#provide-parameterization.

I'll have to admit that I'm not a huge fan of the example shown in the prior link -- primarily because this code is complex enough to step pretty far outside the bounds of what we have traditionally called an "interface". This is something that I mentioned in a prior article (C# 8 Interfaces: Public, Private, and Protected Members), specifically when looking at private members.

This view is still subject to change. On one side, there are people who are pushing for interfaces as abstractions, and on the other side, there are people who are pushing for interfaces as implementation. I'm not sure where we'll end up (or where I'll specifically end up). We need to have discussions about how to take things forward in a way that most developers can be successful.

The Same Thing with a Class
All of the static member functionality that we've seen in this example can be accomplished with a class. The sample project contains a class that does just that: ReaderFactory.

Here is the code from the ReaderFactory.cs file:


The body of the class is identical to the body of the interface that we saw above. And the console application can be updated to use this class instead of the interface, and it will operate just the same.

Class or Interface?
As mentioned above, we won't get into the pros and cons of static members in this article. We are just looking at what is technically available to us.

Since the code shown here includes only static members, I would probably lean toward a class for this particular code. If there were closely-related abstract members, then I might lean toward an interface (or maybe just an abstract class).

For now, I'm exploring what is possible with interfaces in C# 8. But I'm still thinking of interfaces as abstractions (for now).

Wrap Up
Static members in interfaces are quite a change from C# 7:
  • Interfaces can have static members
  • Static methods must have implementation
  • Static fields are now allowed
  • Static fields do not need a default value (but probably should have one)
  • Static fields can be used to provide parameters for other members
There are a lot of changes to interfaces in C# 8. Even with "static" there is more to explore. For example, it is possible to create static constructors in an interface. I still need to take a closer look at what this means and why we might need to do that.

Keep exploring, keep learning. Once we get the technical details down, we can start trying new techniques to see where they lead us.

Happy Coding!