Showing posts with label Interfaces. Show all posts
Showing posts with label Interfaces. Show all posts

Friday, July 9, 2021

A Collection of 2020 Recorded Presentations

2020 was "interesting". One good thing that came out of it is that I had the chance to speak remotely for some user groups and conferences that I would not normally get to attend in person. Many of those presentations were recorded. Here's a list for anyone who is interested

Abstract Art: Getting Abstraction "Just Right" (Apr 2020)

Modern Devs Charlotte

Abstraction is awesome. And abstraction is awful. Too little, and our applications are difficult to extend and maintain. Too much, and our applications are difficult to extend and maintain. Finding the balance is the key to success. The first step is to identify your natural tendency as an under-abstractor or an over-abstractor. Once we know that, we can work on real-world techniques to dial in the level of abstraction that is "just right" for our applications.


I'll Get Back to You: Task, Await, and Asynchronous Methods in C# (Apr 2020)

Enterprise Developers Guild

There's a lot of confusion about async/await, Task/TPL, and asynchronous and parallel programming in general. So let's start with the basics and look at how we can consume asynchronous methods using Task and then see how the "await" operator can makes things easier for us. Along the way, we’ll look at continuations, cancellation, and exception handling.


A Banjo Made Me a Better Developer (May 2020)

Dev Around the Sun

What does a banjo have to do with software development? They both require learning. And picking up a banjo later in life showed me 3 things that I've brought into my developer life. (1) You can learn; a growth mindset removes blockages. (2) You don't have to be afraid to ask for help; experienced banjoists/developers can point you in the right direction. (3) You don't have to be perfect before you share what you've learned; it's okay to show what you have "in progress". In combination, these have made me a better banjo player and a better developer.


I'll Get Back to You: Task, Await, and Asynchronous Methods in C# (Jun 2020)

Houston .NET User Group

There's a lot of confusion about async/await, Task/TPL, and asynchronous and parallel programming in general. So let's start with the basics and look at how we can consume asynchronous methods using Task and then see how the "await" operator can makes things easier for us. Along the way, we’ll look at continuations, cancellation, and exception handling.


What's New in C# 8 Interfaces (and how to use them effectively) (Jun 2020)

Southeast Valley .NET User Group
North West Valley .NET User Group

C# 8 brings new features to interfaces, including default implementation, access modifiers, and static members. We'll look at these new features and see where they are useful and where they should be avoided. With some practical tips, "gotchas", and plenty of examples, we'll see how to use these features effectively in our code.


What's New in C# 8 Interfaces (and how to use them effectively) (Jul 2020)

Tulsa Developers Association

C# 8 brings new features to interfaces, including default implementation, access modifiers, and static members. We'll look at these new features and see where they are useful and where they should be avoided. With some practical tips, "gotchas", and plenty of examples, we'll see how to use these features effectively in our code.


I'll Get Back to You: Task, Await, and Asynchronous Methods in C# (Aug 2020)

Code PaLOUsa

There's a lot of confusion about async/await, Task/TPL, and asynchronous and parallel programming in general. So let's start with the basics and look at how we can consume asynchronous methods using Task and then see how the "await" operator can makes things easier for us. Along the way, we’ll look at continuations, cancellation, and exception handling.


Get Func-y: Understanding Delegates in C# (Oct 2020)

St Pete .NET

Delegates are the gateway to functional programming. So let's understand delegates and how we can change the way we program by using functions as parameters, return types, variables, and properties. In addition, we'll see how the built in delegate types, Func and Action, are waiting to make our lives easier. By the time we're done, we'll see how delegates can add elegance, extensibility, and safety to our programming. And as a bonus, you'll have a few functional programming concepts to take with you.


More to Come!

I have a few more recordings from 2021 presentations, and I'll post those soon. As always, if you'd like me to visit your user group, drop me a line. I'll see what I can do about being there online or in person (once in-person stuff is a bit more back to normal).

Happy Coding!

Thursday, June 24, 2021

New and Updated: "C# Interfaces" Course on Pluralsight

An updated version of my "C# Interfaces" course is now available on Pluralsight.


You can click on the link and watch a short promo for the course. If you are not a Pluralsight subscriber, you can sign up for a free trial and watch the entire course.

"C# Interfaces" title slide with a photo of the author, Jeremy Clark

Description

Code that is easy to maintain, extend, and test is key to applications that can move quickly to meet your users’ needs. In this course, C# Interfaces, you’ll learn to use interfaces to add flexibility to your code. First, you’ll explore the mechanics (“What are interfaces?”) and why you want to use them. Next, you’ll discover how to create your own interfaces to make it easy to change or swap out functionality. Finally, you’ll learn about default member implementation and how to avoid some common stumbling blocks. When you’re finished with this course, you’ll have the skills and knowledge of C# interfaces needed to write application code that is easy to maintain, extend, and test.

What's Different

For those who have seen a previous version of this course, here are a few key things that have been added or updated:
  • New section on default implementation
  • A new comparison between interfaces and abstract classes
  • New demo on using configuration and compile-time binding
  • Updated content for dynamic loading of assemblies
  • All code samples are cross-platform friendly (Windows, Linux, macOS)
For a full list of changes, you can check out the change log: "C# Interfaces" Change Log.

Additional Resources

I have added an "Additional Resources" repository on GitHub that includes:
  • The latest code samples (to be updated when .NET 6 drops later this year)
  • Instructions on running the samples with Visual Studio 2019 and Visual Studio Code
  • A list of relevant files for each code sample
  • Articles and links for further study

There are also a couple of samples that I was not able to fit into the course. So stay tuned to this blog; I'll be posting those in the near future.

Happy Coding!

Sunday, October 18, 2020

Abstract Classes vs. Interfaces in C# - What You Know is Probably Wrong

A little over a year ago, C# 8 changed a lot of things about interfaces. One effect is that the technical line between abstract classes and interfaces has been blurred. It's been a year, and I haven't seen much online about this, so let's take a closer look at how things have changed.

To show the changes, here is a slide I used to use to show the differences. 4 of the 5 differences no longer apply:

Side-by-side list of differences between interfaces and abstract classes with 4 of the 5 differences being crossed out.

A lot of folks are not aware of these changes, so let's go through what we used to know about the differences between abstraction classes and interfaces and see if it still holds up today.

Note: For more information on the changes to interfaces in C# 8, you can take a look at the code and articles in this repository: https://github.com/jeremybytes/csharp-8-interfaces. There are also links to videos that demonstrate the new features (and some of the pitfalls).


Difference #1

An abstract class may contain implementation code. An interface may not have implementation code, only declarations.

Status: No Longer True

Interfaces in C# 8 can have implementation code -- referred to as "default implementation". For example, an interface method can have a body that provides functionality. This functionality is used by default if the implementing class does not provide its own implementation.

An interface can also provide default implementation for properties, but due to limitations with property setters, this only makes sense in specific circumstances.

More information: C# 8 Interfaces: Properties and Default Implementation.


Difference #2

A class may only inherit from a single abstract class, but a class may implement any number of interfaces.

Status: Still True

C# still has single inheritance when it comes to classes. But because a class can implement any number of interfaces, and those interfaces may have implementation code, there's a type of multiple inheritance that is available through interfaces.

When calling default implementation members, the caller must use the interface type (just like when a class has an explicit implementation). This avoids the "diamond problem", which is where two base classes provide the same method. By specifying the interface, this avoids the runtime having to make decisions of which method to use.


Difference #3

Abstract class members can have access modifiers (public, private, protected, etc.). Interface members are automatically public and cannot be changed.

Status: No Longer True

In C# 8 interface members can have access modifiers. The default is public (so existing interfaces will still work as expected). But interfaces can now have private members that are only accessible from within the interface itself. These may be useful for breaking up larger methods that have default implementation.

Note: Protected members are also possible, but because of the weirdness of implementation, I have not found a practical use for them.

More information: C# 8 Interfaces: Public, Private, and Protected Members.


Difference #4

Abstract classes can contain fields, properties, constructors, destructors, methods, events, and indexers. Interfaces can contain properties, methods, events, and indexers (not fields, constructors, or destructors).

Status: Mostly True

This is still mostly true. When it comes to instance members, interfaces can contain only properties, methods, events, and indexers. Instance fields and constructors are still not allowed.

But, interfaces can contain static members (see "Difference #5" below). This means that interfaces can have static fields, static constructors, and static destructors.


Difference #5

Abstract classes may contain static members. Interfaces cannot have static members.

Status: No Longer True

As noted above, interfaces in C# 8 can have static members. A static method in an interface works exactly the same way as a static method on a class. 

The same is true for static fields. Yes, fields are allowed in an interface, but only if they are static. As a reminder, a static field belongs to the type itself (the interface) and not any of the instances (meaning, instances of a class that implement the interface). This means that it is a shared value, so you want to use it with care. Note that this is exactly how static fields work on classes, so if you've used them there, you already know the quirks.

More information: C# 8 Interfaces: Static Members.

Also, having a "static Main()" method is now allowed in an interface. If you want to drive your co-workers crazy, here's one way to misuse this: Misuing C#: Multiple Main() Methods.


Technical Differences vs. Usage Differences

So if we look at the 5 technical differences between abstract classes and interfaces, 3 of them are no longer true, and 1 of them has some new caveats.

This really blurs the technical line between abstract classes and interfaces. When it comes to actually using abstract classes and interfaces, I'm currently sticking with my previous approach.

If there is *no* shared code between implementations...
I use an interface. Interfaces are a good place to declare a set of capabilities on an object. I like to use interfaces in this case (rather than a purely abstract class) because it leaves the inheritance slot open (meaning, the class can descend from a different base class if necessary).

If there *is* shared code between implementations...
I use an abstract class. An abstract class is a good place to put shared implementation code so that it does not need to be repeated in each of the descending classes.

Why not use interfaces with defaults for shared implementation? Well, there are a lot of interesting things that come up with default implementation, such as needing to refer explicitly to the interface and not the concrete type. Using the interface type is a generally a good idea (and I recommend that), but when it is forced on you, it can be confusing.

Another limitation has to do with properties. Because interfaces cannot have instance fields, there is no way to set up real properties using default implementation. Abstract classes can have fields, so we can put full properties and automatic properties into an abstract class that are then inherited by descending classes.

My approach is a personal preference based on how interfaces and abstract classes have been used in the past. I'm all for language and technique evolving, but this is a slow process. And until certain practices become widespread, I like to use "the path of least surprise". In this case, it means keeping a logical separation between abstract classes and interfaces even though the technical separation has been blurred.

Mix Ins
One other way that default implementation can be used is for mix ins. This is where an interface *only* contains implemented members. This functionality can then be "mixed in" to a class by simply declaring that the class implements the interface. No other changes to the class are necessary.

This is an interesting idea, and I still have to explore it further. Personally, I wish this functionality got a new keyword. It muddies things up a bit when we try to define what an interface is.

Wrap Up

So if we look at the 5 differences between abstract classes and interfaces, we find that most of them are not longer true starting with C# 8.

If you'd like to see the implications of these changes, take a look at the articles available here: https://github.com/jeremybytes/csharp-8-interfaces or here: A Closer Look at C# 8 Interfaces

And if you'd like a video walkthrough with lots of code samples, you can take a look at this video on YouTube: What's New in C# 8 Interfaces (and how to use them effectively). This is from a talk I did for the Phoenix AZ area user groups.


Happy Coding!

Thursday, July 30, 2020

Go and Interfaces

I've restarted my adventures in Go (golang). Based on the experience that I've had in the meantime, I have a better appreciation of many of the language features.

Specifically, I've become a fan of how Go handles interfaces compared to C#. (Interfaces in C# have become a bit of a complicated mess. I've written a series of articles about the recent changes (A Closer Look at C# 8 Interfaces) and have done some recent user group talks on the changes that come out over 2 hours long (recording of the Tulsa .NET User Group)).
I find that the Go minimalist approach to interfaces can make things easier to code.
So, let's look at an example that I recently worked through.

Go Interfaces

Before looking at the example, let's look at how interfaces work in Go.

Here is the "io.Writer" interface that is part of the Go io package (docs: io.Writer):

type Writer interface {
    Write(p []byte) (n int, err error)
}

This declares the "Writer" interface which has one method: "Write".

The "Write" method takes a byte array as a parameter, and returns an int (the number of bytes written) as well as an error (in case any errors occur). As a side note, the "int, error" return type is a common pattern in Go.

The interesting bit (and where things differ from C#) is how types implement this interface. Any type that wants to implement the "io.Writer" interface just needs to have the "Write" method itself. It does not need to declare itself as an "io.Writer", it just has to have the method.

This means that any type with a "Write" method that matches this signature is automatically an "io.Writer".

Let's see this in action.

Lissajous Curve

My example code comes from The Go Programming Language by Alan A. A. Donovan and Brian W. Kernighan (Amazon link). One of the early tutorials is to create a Lissajous curve. Here's an gif showing what that is:


The code is a bit of math that gets output to an animated gif. I have this code on GitHub (jeremybytes/go-programming-language), specifically, we'll look at the "ch1/lissajous" project. The code is all in a single file, "main.go" (link on Github: main.go).

Here's the code for creating the gif (starting at line 44 of main.go):


This has the constants collapsed, but you can check the full code for details.

There are 2 bits I want to point out here: First, the "lissajous" function takes an "io.Writer" as a parameter. Finally, the "gif.EncodeAll" method at the bottom uses the "io.Writer" parameter to produce the output.

Using the Lissajous Function

We use the lissajous function in the "main" function (the "main" function gets executed when we run the Go program).

The most basic call is on line 41 (from main.go):

lissajous(os.Stdout)

This passes "os.Stdout" to the "lissajous" function. The result is that the gif is written to Standard Out (which is the console if we just run this as a console application).

Writing a gif to the console is not very useful, but we can pipe the output of our program to a file like this:


.\lissajous.exe > out.gif

This will pipe the output of the program to a file named "out.gif".

A few notes here. First, the ".\lissajous.exe" command has a backslash. That's because I'm running this on Windows 10. Next, this command doesn't actually work in PowerShell on Windows 10. The "out.gif" file is created, but it's not an actual gif (meaning, nothing can display it). I'm sure this is PowerShell weirdness (I show how I got around this in just a bit). When I run this command on macOS (with a slash instead of a backslash), it works just fine. The "out.gif" file is a valid animated gif.

A Web Server

One of the cool things about Go is that we can spin up a web server with just a few lines of code. Here is a bit of code that we can add to the "main" method to serve up the animated gif (starting on line 27 of main.go):


This sets up an HTTP handler that calls the "lissajous" method. On the second line, note that the handler is a function that takes an "http.ResponseWriter" as a parameter. This type implements the "io.Writer" interface, so all we need to do is pass it to the "lissajous" method.

We can run the application with the "web" parameter like this:


.\lissajous.exe web

Then, we can navigate a browser to "http://localhost:8000" to get this result:


Pretty cool. (Okay, so it's cooler if you do it yourself because this screenshot doesn't show the animation.)

Note: To kill the web server, use "Ctrl+C" on the console.

Appreciating Interfaces

So now we get to the part where I learned to really appreciate interfaces. As I noted above, piping the Standard Out to a file doesn't work in PowerShell for some reason (although it does work on the macOS terminal). I decided to add another parameter to the application so that it would go straight to a file.

So just like the "web" parameter would start a web server, a "file" parameter would output it to a file.

And this is where I got a bit confused and started fumbling a bit. I was thinking a bit more linearly, so I was thinking about how I could create a file, open the file, and then write whatever came from the "lissajous" function to that file.
I was looking up examples on how to write to a file in Go. But I was on completely the wrong track for this application.
I was getting a bit frustrated because it seemed way too difficult. And that's when I finally looked up exactly what "io.Writer" was (back to the doc: io.Writer):

type Writer interface {
    Write(p []byte) (n int, err error)
}

That's when I realized that I was dealing with an interface, and not some base type. (I was too mired in C# thinking at the time.)

So then, I looked at the "os.File" type (doc here: os.File). Scrolling down a bit, I saw that "File" has a "Write" method:


This method signature matches the io.Writer interface!

Once I had made the connection, the code fell together pretty easily. Here is that part of the "main" method (starting with line 35 in main.go):


The "os.Create" returns a File pointer and an error (docs: os.Create). I assign the file pointer to "gf" (for "gif file") and use an underscore to discard the error object.

Note: I'm skipping error checking here. If you try to compile when you have an unused variable, the compile will fail (a nice safety net). This means that you will often see discards in function and method calls. In this case, we are not checking errors, so we discard the error item that comes back from "os.Create".
Since the file is an "io.Writer", we can pass it directly to the "lissajous" function. This is very convenient (once you start thinking this way).
Now, we can run the application with the "file" argument:


.\lissajous.exe file

And we get a valid animated gif on the file system named "out.gif".


Wrap Up

So, I took the long way around to a convenient solution. But it made me appreciate interfaces in Go that much more.

I really like the minimal approach to interfaces in Go. I don't have to look at a type signature to figure out "is this an io.Writer?" All I need to do is look at the type members themselves. If it has members that satisfy the interface, then I know I can use it wherever that interface is called for.

There are a lot of things to get used to in Go, and there are some conventions to learn. As an example of a convention, a method / function / type that starts with a capital letter is exported (similar to "public" in C#; it can be used from other packages). If it starts with a lower-case letter, it is not exported (similar to "private" in C#).

There are lots of interesting ideas in Go. I'm enjoying my second look at the language.

Sunday, January 5, 2020

Using Type.GetType with .NET Core / Dynamically Loading .NET Standard Assemblies in .NET Core

The Type.GetType() method lets us load a type from the file system at runtime without needing compile time references. Because of the way that assemblies are loaded in .NET Core, this behaves differently than it did in .NET Framework.

Update Jan 13, 2020: The Type.GetType() method has not changed, but runtime behavior regarding assembly loading has changed. That doesn't negate the workaround shown here, so feel free to keep reading. For more information see the next article: Type.GetType() Functionality Has Not Changed in .NET Core.

Short Version
In .NET Framework, calling Type.GetType() with an assembly-qualified name...
  • Loads the specified assembly from the file system
    Note: "from the file system" is observed behavior, but technically not correct -- see the followup article mentioned above for more details. 
  • Returns a Type object
In .NET Core, calling Type.GetType() with an assembly-qualified name...
  • Does *NOT* load the specified assembly from the file system
  • If the assembly is not already loaded, GetType() returns null
  • If the assembly is already loaded, GetType() returns the Type object
This means that we need to take extra steps in .NET Core in order to use Type.GetType() with .NET Core applications. And things get more interesting if we are loading .NET Standard assemblies.

Note: The code for this article comes from my talk on C# interfaces (IEnumerable, ISaveable, IDontGetIt: Understanding .NET Interfaces). The code is in 2 separate GitHub repositories. The .NET Framework code is at GitHub: jeremybytes/understanding-interfaces. The .NET Core code is at GitHub: jeremybytes/understanding-interfaces-core30.
In the spirit of "the fastest way to get a right answer is to post a wrong one", I'm putting down my thought processes in solving an issue. If there is an easier way (such as setting a flag or adding a config setting), please let me know in the comments.
Why Dynamically Load Types?
I have used dynamic loading of types for 2 primary scenarios: (1) swapping one set of functionality for another, and (2) plugging in business rules. In either of these scenarios, we do not need to have the specifics of the dynamically loaded types available at compile time. Things can be figured out at run time.

In the first scenario (and the code sample we'll look at today), I have changed out data-access code from one system to another. For example, to get data from a SQL database, a web service, or some other location. This is particularly helpful when there are multiple clients using the same application with different data storage systems.

In the second scenario, I deployed an application with the option of adding or changing certain business rules at a later time. The business rules follow a specific interface and are stored in a separate assembly (or multiple assemblies). At runtime, the application loads the business rules from the file system. This made it easy to update existing rules, add rules, or remove rules without needing to recompile or redeploy the application; only the business rule files were affected by updates.

Dynamic Loading with .NET Framework
I ran across this issue when I was moving a WPF application from .NET Framework 4.7 to .NET Core 3.0. We'll start with code from the .NET Framework repository mentioned above (GitHub: jeremybytes/understanding-interfaces), specifically the "completed" code in the "04-DynamicLoading" project (completed/04-DynamicLoading).

This application dynamically loads a data repository that can get data from a text file (comman-separate values (CSV)), a web service (HTTP/JSON), or a SQL database (SQLite local db). The application does not know anything about these data repositories at compile-time. Instead, it loads a repository from the file system based on configuration.

Here is the code that dynamically loads the repository (from the RepositoryFactory.cs in the PeopleViewer project):


The "GetRepository" method returns IPersonRepository -- this is the interface that represents the data repository.

The first line of the method gets the assembly-qualified name from configuration. Here's the configuration section for the CSV Repository (from the App.config file of the PeopleViewer project):


This lists the assembly-qualified name for the repository. This consists of the following parts:
  • Fully-qualified Type Name: PersonRepository.CSV.CSVRepository
  • Assembly Name: PersonRepository.CSV (this is "PersonRepository.CSV.dll" on the file system)
  • Assembly Version: 1.0.0.0
  • Assembly Culture -- this is used for localization (which we haven't implemented here)
  • Assembly Public Key Token -- this is used for strongly-named assemblies (which we haven't implemented here)
The next line in our method makes a call to Type.GetType() with this assembly-qualified name. GetType will find the assembly on the file system (by default, it looks in the same folder as the executable). Then it loads the assembly and pulls out the information for the Type object.

Activator.CreateInstance will create an instance of the Type using a default constructor. In this case, it will be a CSVRepository.

The last 2 lines cast the new instance to the correct interface and return it.

Getting the Repository Assemblies
One other piece is that we need to get the CSVRepository assembly to the executable folder somehow. The project does not have any compile-time references to the assembly, so we need to do this manually.

We have the repositories (and all of their dependencies) in a folder at the solution level called "RepositoriesForBin" (you can look at the contents of RepositoriesForBin on GitHub). Here's a bit of a screenshot from Windows File Explorer:


This snippet shows the "PersonRepository.CSV.dll" file that we're using here. In addition, we have files for the web service repository, the SQL database repository, and all of the dependencies for those repositories.

To get these into the executable folder, the PeopleViewer project has a post-build step. (It's kind of buried in the PeopleViewer.csproj file on GitHub -- it's easier to look at this in the Visual Studio project properties). Here is the post-build section of the project properties:


This copies files from the from the "RepositoriesForBin" folder to the output folder (including any sub-folders).

For more information on build events, take a look at "Using Build Events in Visual Studio to Make Life Easier".

Running the Application
When we run the application, we get data using the CSV repository (which gets data from a text file on the file system).


Changing the Data Source
If you're curious about how the dynamic loading works. Shut down the application and open the executable file in File Explorer (this is in the PeopleViewer/bin/Debug folder).

Run "PeopleViewer.exe" by double-clicking it from File Explorer. You will see the same results as above.

Shut down the application, and then edit the "PeopleViewer.exe.config" file on the file system using your favorite text editor. Comment out the section for the "CSV Repository" and uncomment the section for "SQL Repository". Save and close the file.

Now when you re-run the application, it will use the SQL database instead of the text file.

In real life, we would not be doing this on a single machine. However, think of the scenario where we have multiple clients. For each client, we give them just the assemblies that they need for their particular environment. If a new client has a different data store, that's fine. We create a repository assembly and give it to that client. We do not need to recompile the application or deal with multiple versions deployed at different client sites.

Anyway, on to .NET Core.

Converting to .NET Core
For the .NET Framework project, the WPF application (PeopleViewer) is a WPF application. The web service (People.Service) is an ASP.NET Core 2.2 API. The interface project (PersonRepository.Interface) is a .NET Standard 2.0 project. All of the repository files are .NET Standard 2.0 as well.

Part of the reason for using .NET Standard project for many things is that I knew I would be moving the WPF application to .NET Core once .NET Core 3.0 was released. And that's what I did.

I moved the WPF application and the web service to .NET Core 3.0. And I moved the libraries (including the repositories) to .NET Standard 2.1.

One other thing I did with this application was change all references from "Repository" to "Reader". Since the operations for the repositories are read-only, the term "reader" is more appropriate.

The completed code is on GitHub (jeremybytes/understanding-interfaces-core30), specifically in the "completed/04-DynamicLoading" folder. Note that this repository has the completed code, so you won't be able to follow along with the interim code (you can contact me for details if you'd really like to follow along).

Broken Code in .NET Core
Unfortunately, if we take the "GetRepository" method (now called "GetReader") straight across, the code does not work.

If we run the application, we get an exception:


The "Activator.CreateInstance" method is giving us an ArgumentNullException. This means that the "readerType" variable that we have here is null.

"GetType" is not returning what we want. Here are the values of "readerTypeName" and the "readerType" in the debugger:


This shows that the "readerTypeName" variable is populated with the assembly-qualified name that we expect. So that's fine.

But the "readerType" that is returned from the GetType method is null.
GetType does not automatically load an assembly in .NET Core like it does in .NET Framework.
Frustration and Reasoning
This is where I went through a bit of frustration. When checking the documentation for "GetType", there is currently (as of Jan 5, 2020) no indication that it works differently. Here is a screenshot of the beginning of the "Remarks" section for Type.GetType (link (which will hopefully be updated by the time you read this): https://docs.microsoft.com/en-us/dotnet/api/system.type.gettype?view=netcore-3.1#System_Type_GetType_System_String_):


Note that I do have ".NET Core 3.1" selected for the Version. Here is the start of the "Remarks" text:
"You can use the GetType method to obtain a Type object for a type in another assembly if you know its assembly-qualified name, which an be obtained from AssemblyQualifiedName. GetType causes loading of the assembly specified in typeName." (emphasis mine)
So, according to the documentation, this should work.

Assembly Loading and Unloading
The reason that this does not work is that the assembly loading mechanism was changed for .NET Core. This was done for a couple of reasons. First, we can set up different assembly load contexts; this lets us load different versions of assemblies into different contexts in the same application. This was not really possible before. Second, we can unload assemblies after we're done with them. Again, this is something that was very difficult to do before.

Manually Loading an Assembly
In getting this to work, my first step was to manually load the assembly by hard-coding the value. Here is the code for that.


Before calling the "GetType" method, this code loads the CSV assembly into the context using "AssemblyLoadContext.Default.LoadFromAssemblyPath". This will load the assembly into the default context (which is the main one that the application uses). The parameter is the assembly file name with the full path.

For the path, there is an assemblyPath variable that is set to the current location of the executable (AppDomain.CurrentDomain.BaseDirectory) with the file name appended (PersonReader.CSV.dll).

This gets us a working application:


But it is of limited usefulness since the CSV reader assembly is hard-coded.

A Different Approach
At this point, I figured that I could try to parse the file name out of the assembly-qualified name that we already have in the configuration file, or I could take a different approach.

When we manually load an assembly into the context like we did above, we also get a reference to that assembly. This means that instead of using "GetType" to locate a type, we can poke into the assembly directly using reflection.

For this approach, I made a few changes to configuration, output folders, and code.

Note: this is not the final version of the code, but you can find it by looking at a particular commit in GitHub: commit/49dc7a33d8071e9eef83d9e1a1d7bba5c3de50cb.

New Configuration
Rather than having the full assembly-qualified name of the type, I created settings for just the parts that I needed. Here is the new configuration (in the App.config file for the commit mentioned above):


Now we have a "ReaderAssembly" key with a value of "PersonReader.CSV.dll" -- the name of the file on the file system. We also have "ReaderType" which is "PersonReader.CSV.CSVReader" -- the fully-qualified name of the reader type.

New Output Folder
In addition, since we will no longer rely on "GetType" being able to find files in the executable folder, I decided to move the reader files to a separate sub-folder in the output. This makes it easier to keep track of the reader assemblies, particularly if we need to remove or change the files.

Along with the new output folder comes updated post-build steps. These are in the PeopleViewer.csproj file for the commit mentioned above. Here is the view from Visual Studio, which is a bit easier to read:


This has 2 copy steps. The first step copies files from the "AdditionalFiles" folder into the output folder. This folder contains the data files that are used by the readers, specifically People.txt (for the CSV reader) and People.db (for the SQL reader).

The next step copies files from the "ReaderAssemblies" folder to a "ReaderAssemblies" subfolder in the output. This contains the dlls for the readers along with the dependencies.

New Code
Along with the new configuration and output location, we have some new code to dynamically load the specified data reader. This is in the ReaderFactory.cs file for the commit mentioned above:


Let's walk through this code.

First we get the "ReaderAssembly" value from configuration. As a reminder, this is "PersonReader.CSV.dll".

Next, we create the full directory path to that file by taking the "BaseDirectory" (where the executable is), appending the new "ReaderAssemblies" subfolder, and then adding the name of the file.

As a side note, the "Path.DirectorySeparatorChar" will pick the correct character for the operating system. So in Windows, it will use the backslash; in Linux and macOS, it will use the forward slash.

Notice that after calling "LoadFromAssemblyPath", we store the return value as "readerAssembly". This is the assembly that we just loaded.

The next step is to get the "ReaderType" from configuration. As a reminder, this is "PersonReader.CSV.CSVReader".

Next we get the reader type out of the loaded assembly. This code uses a little bit of LINQ to reflect into the assembly. "ExportedTypes" is a collection of all of the publicly visible types that are in the assembly. In the query, we go through the types and try to find one that matches the value from configuration. If the type is not found, this method returns null.

The rest of the method is what we had before. Once we have the Type, we can use the Activator to create an instance, and then we cast it to the appropriate type.

Working Code (sort of)
This code seems like a good approach. We can use configuration to decide which assembly and type to load. And when we run the application, it works!


The CSV reader works just fine, but we run into a problem if we try to use one of the other reader types.

Let's update the configuration to use the web service reader. (In the App.config file for the commit mentioned above, comment out the CSV section and uncomment the Service section):


This sets the values for "ReaderAssembly" and "ReaderType" to "PersonReader.Service.dll" and "PersonReader.Service.ServiceReader" respectively.

Unfortunately, this breaks the application. If we run the application and click the button, we get an exception:


This is a "file not found" exception. And the details tell us that it is trying to load the assembly for Newtonsoft.Json version 12.0.0.0. The service reader has a dependency on Newtonsoft.Json.

That brings us to the next problem: loading dependencies.

Assembly Dependencies
In searching for a solution, I came across a tutorial about adding plugin support: Create a .NET Core application with plugins.

This tutorial addresses dependencies. Unfortunately, the described solution does not work for the current code. In the section "Plugin target framework recommendations" we see the following (screenshot and text in case it gets updated):


"Because plugin dependency loading uses the .deps.json file, there is a gotcha related to the plugin's target framework. Specifically, your plugins should target a runtime, such as .NET Core 3.0, instead of a version of .NET Standard. The .deps.json file is generated based on which framework the project targets, and since many .NET Standard-compatible packages ship reference assemblies for building against .NET Standard and implementation assemblies for specific runtimes, the .deps.json may not correctly see implementation assemblies, or it may grab the .NET Standard version of an assembly instead of the .NET Core version you expect." (emphasis mine)
This plugin solution relies on ".deps.json" files to resolve dependencies. And there's our first problem.

.deps.json
The .deps.json file has the dependencies for an assembly. For example, when we build the PeopleViewer application, we get the following output:


In addition to the PeopleViewer.exe (which calls PeopleViewer.dll), we also have PeopleViewer.deps.json. By looking inside this file, we can see the following:


This has a "dependencies" section that shows a dependency on "PersonReader.Interface" version 1.0.0. (This is the interface project that we saw above). Because this is included, that assembly can be loaded along with the PeopleViewer assembly.

But our data reader assemblies do not have .deps.json files:


These assemblies are .NET Standard assemblies. As noted in the plugin tutorial, the dependencies for .NET Standard assemblies cannot be generated without knowing what .NET environment it will be running under. For example, the service reader may need a different version of Newtonsoft.Json when run from .NET Framework compared to running in .NET Core.

Options
To go down the path of the sample plugin architecture, I would need to change the data reader projects to .NET Core from .NET Standard. That is not something that is always practical depending on how the projects are being used.

Additionally, the plugin architecture seemed to be quite a bit more than I needed for this application.

Since all of the reader assemblies and dependencies are in a separate folder, I can take a different path. Instead of trying to figure out how to get the dependencies to load automatically, I can just load them manually.

Manually Loading Assemblies
In the previous code, we manually loaded the one data reader assembly based on configuration. To load the dependencies, we will load all of the assemblies that are in the "ReaderAssemblies" folder.

Here is the code for a "LoadAllAssemblies' method (from the "ReaderFactory.cs" file for the commit mentioned above):


In the first line, we build the path to the "ReaderAssemblies" folder.

Next, "Directory.EnumerateFiles()" will give us an enumeration of all the file names that match our search criteria. In this case, we ask for all files that end with ".dll". Also, we only search the top folder (not any subfolders).

Then we use "foreach" to loop through all the file names and load them into the default context. If there are any files that can't be loaded, then we just skip them.

Assumption
This has the assumption that all of the .dlls in this folder are ones that we want to load. This is a bit easier to do since we have a separate folder. If the reader assemblies were still in the root folder (like we had initially), I would be much more reluctant to try this approach.

Working Code
To get the code working, we call "LoadAllReaderAssemblies" at the top of our factory method (from the "ReaderFactory.cs" file for the commit mentioned above):


And now the application works with the service data reader as well:


Note: If you run this application yourself, you will also need to start the service. To start the service, open a command prompt to the "People.Service" folder and type "dotnet run". For more information on .NET Core services, check out this tutorial: Get Comfortable with .NET Core and the CLI.

Duplicated Code
With the updated solution in place, we have some unnecessary code. Let's take another look at the "GetReader" method (same as above):


In this case, the reader assembly gets loaded twice. When we call "LoadAllReaderAssemblies", everything in that folder is loaded, including the one for the data reader.

Then the next lines are concerned about getting a reference to the "Assembly" object that represents the reader assembly. To do this, we end up loading the data reader assembly a second time.

Rethinking the Solution
Let's go back to the initial problem: Type.GetType() does not automatically load an assembly.

But we saw that it still works when we manually loaded the assembly. Remember this code?


When we manually loaded the "PersonReader.CSV.dll" assembly, GetType worked just fine.

Now that we are loading the reader assembly and all of its dependencies, we can go back to that solution.

Back to Square One
With a better understanding of what's going on, we can go back to where we were initially. We can take our original code and add "LoadAllReaderAssemblies" to the top. Here is that code (in the ReaderFactory.cs file in the final code):


With this, we also need to go back to the original configuration (from the App.config file in the final code):


After all of the assemblies are loaded, GetType returns the Type object that we expect, and the rest of the code works as expected.

Running the application with this configuration gets data from the CSV text file:


And we can change the configuration to use the service:



Wrap Up
So we took a bit of a roundabout way to get back to where we started. But we learned some things along the way.
  • With .NET Core, we need to explicitly load assemblies.
  • With dynamically-loaded .NET Standard assemblies, we need to explicitly load any dependencies.
There are also some things to look into further.
  • Using .NET Core (or other specific framework) projects gives us a .dep.json file that specifies dependencies.
Moving to .NET Core is pretty smooth for the most part. But there are things that pop up that can be frustrating. Eventually we'll have all of those things catalogued, and conversions will be easier.

Happy Coding!

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!