Thursday, January 14, 2021
Wednesday, January 13, 2021
Go functions can return multiple values.
Multiple Return Values
Using Multiple Return Values
Different from Tuples
Tuesday, January 12, 2021
Go has "error" to represent problems that can potentially be handled and "panic" for problems that force an application to exit.
Exceptions in C#
"error" in Go
Convention: A function returns both data and an error. A non-nil error value means that an error occurred.
Convention: Check the error before using any data returned from a function.
Convention: If there is an error, short-circuit the rest of the function.
Convention: Specific error messages are prepended to an existing error.
Convention: Error messages should start with a lower-case letter.
Convention: Error messages should not have line breaks or periods.
Sample Error Message
Error Handling Philosophy
- A function returns both data and an error. A non-nil error value means that an error occurred.
- Check the error before using any data returned from a function.
- If there is an error, short-circuit the rest of the function.
- Specific error messages are prepended to an existing error.
- Error messages should start with a lower-case letter.
- Error messages should not have line breaks or periods.
What I Find Interesting
Monday, January 11, 2021
The "defer" statement lets us ensure that code runs before a function exits.
Example of "defer"
Comparison to try/finally
Mutiple Deferred Items
Where Deferred Items are *Not* Run
What I Like about "defer"
Sunday, January 10, 2021
The "for" statement is used for all loops in Go.
Looping with an Indexer
- Go does not have parentheses around the condition.
- Go has the opening brace on the same line as the "for".
- Go requires the braces even if the body only has 1 line of code.
"break" and "continue"
Sunday, October 18, 2020
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:
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).
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.
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.
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.
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.
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.
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.
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.
Friday, September 11, 2020
When we use "await" in C#, we can use the typical try/catch blocks that we're used to. That's a great thing, and it makes asynchronous programming a lot easier.
One limitation is that "await" only shows a single exception on a faulted Task. But we can use a little extra code to see all of the exceptions.
Let's take a closer look at what happens when we "await" a faulted Task and how we can take more control if we need it.
Tasks in C# are very powerful. They can be chained together, run in parallel, and set up in parent/child relationships. With this power comes a bit of complexity. One part of that is that tasks generate AggregateExceptions -- basically a tree structure of exceptions.
When we "await" a Task, it unwraps the AggregateException to give us the inner exception. This makes our code easier to program, and often there will only be one inner exception. But when there are multiple exceptions, we lose visibility to them.
This topic came up in a virtual training class that I did this past week. We were looking at parallel processing that included exception handling. We didn't have time to dig into this in the class, so I did some experimentation after.
Note: the code for this article (and the rest of the virtual training class) is available on GitHub: https://github.com/jeremybytes/understanding-async-programming.
The projects that we'll use today are "People.Service" (the web service), "TaskAwait.Library" (a library with async methods), and "TaskException.UI.Console" (a console application).
If you're completely new to Task, you can check out the articles and videos listed here: I'll Get Back to You: Task, Await, and Asynchronous Methods in C#.
For this sample code, we have a console application that calls a web service multiple times. It is called for each record we want to display (for this scenario, we might only want a handful of records rather than the entire collection).
Starting the Service
If you want to run the application yourself, you'll need to start the "People.Service" web service. To do this, navigate your favorite terminal to the "People.Service" folder and type "dotnet run".
This shows the service listening at "http://localhost:9874". The specific endpoint we're using here is "http://localhost:9874/people/3" (where "3" is the id of the record that we want).
Running in Parallel with Task
The "TaskException.UI.Console" project has a single "Program.cs" file. This is where we have our parallel code. Here is an excerpt from the "UseTaskAwaitException" method (starting on line 61 of the Program.cs file):
This section sets up a loop based on the IDs of the records we want to retrieve.
The loop starts with a call to the "GetPersonAsyncWithFailures" method that returns a task (we'll look at this method in a bit). Note the "WithFailures" is there because I created a separate method to throw arbitrary exceptions.
The resulting task is added to the a task list that holds a reference to all of the tasks.
Then we set up a continuation on that task. When the task is complete, it will output the record to the console. Note that this is marked with "OnlyOnRanToCompletion", so this continuation only runs on success; it will not run if an exception is thrown.
As a last step in the loop, we add the continuation task to our list of tasks.
Because nothing is "await"ed in this block of code, all of the tasks (9 altogether) are generated very quickly without blocking. So these will run in parallel.
Outside of the loop, we use "await Task.WhenAll(taskList)" to wait for everything to complete.
This will wait for all 9 tasks to complete before continuing with the rest of the method.
The Happy Path
When no exceptions are thrown, this outputs 9 records to the console. Here is a screenshot from the "Parallel.UI.Console" project (which calls a method that does *not* throw exceptions):
This shows us all 9 records. Now let's see what happens when there are exceptions.
Throwing Arbitrary Exceptions
As mentioned above, the method we're calling for testing throws some arbitrary exceptions. Our method is called "GetPersonAsyncWithFailures". Here is an excerpt from that (starting on line 75 of the TaskAwait.Library/PersonReader.cs file):
This method has two "if" statements that throw exceptions. If the "id" parameter is "2", then we throw an InvalidOperationException. If the "id" parameter is "5", we throw a "NotImplementedException".
These exceptions are completely arbitrary. We just need multiple calls to this method to fail so we can look at the aggregated exceptions.
"await Task.WhenAll" Shows One Exception
As a reminder, here's the code that we're working with (starting on line 61 of the Program.cs file in the "TaskException.UI.Console" project):
As noted above, the "foreach" loop will run the async method 9 times (with 9 different values). Two of these values will throw exceptions.
We don't need to look for these exceptions in the continuation since we marked it with "OnlyOnRanToCompletion" -- the continuation only runs for successful tasks.
The good news is that when we "await" a faulted Task (meaning, a Task that throws an exception), the exception is automatically thrown in our current context so that we can use a normal "try/catch" block.
When we use "await Task.WhenAll(tasklist)", it will throw an exception if any of the tasks are faulted.
Since we have 2 faulted tasks here, that's exactly what happens.
Here's a bit more of the "UseTaskAwaitException" method (starting on line 76 of the Program.cs file);
The top of this block shows the "await Task.WhenAll" call. Then we have catch blocks to handle cancellation or exceptions. The "OutputException" method shows several of the properties of the exception on the console (the details are in the same "Program.cs" file if you're interested).
Let's run this method to see what happens. To run the application, navigate (using your favorite terminal) to the "TaskException.UI.Console" folder and type "dotnet run".
This will bring up the console application menu:
The code we've seen so far is part of option #1 "Use Task (parallel - await Exception)". If we press "1", then we get the following output:
The output shows us 7 records (since 2 of the 9 failed). And it shows us the "InvalidOperationException" that is thrown for ID 2.
But it does *not* show us the other exception that was thrown.
We'll dig the exception out of the Task directly and look at its values. Then it will be a little more clear what "await" is doing here.
Getting All of the Exceptions
As mentioned at the beginning, Task is very powerful. Because of the various ways that we can chain tasks or run them in parallel, Task uses what's known as an "AggregateException". This contains all of the exceptions that happen in relation to the task.
To see this we will do something a little different with the "Task.WhenAll" method call.
"Task.WhenAll" returns a Task (and this is why we can "await" it). But we can also set up our own continuation to see the details of what happened.
We have a separate method where we've done just that. Let's look at the loop on the "UseTaskAggregateException" method (starting on line 106 of the Program.cs file):
The "foreach" loop is that same as the prior method, but the "await" is different.
The continuation that is set up here is marked as "OnlyOnFaulted", so it will only run if at least one of the Tasks throws an exception. The "task.Exception" property has the AggregateException that is mentioned above.
The "OutputException" method show different parts of the exception. The details are in the "Program.cs" file if you're interested. Otherwise, we can look at the output to see what is in the exception.
Note: we are still using "await", but we are not awaiting the "WhenAll". Instead, we are awaiting the continuation task on "WhenAll". This will still pause our method until all of the tasks are complete (including this final continuation). It can get a bit confusing when mixing "await" with directly using Task.
If we re-run the console application and choose "2" this time (for "Use Task (parallel) - view AggregateException"), we get the following output:
The output shows the 7 successful records, but the exception is different from what we saw earlier.
Instead of the "InvalidOperationException", we get an "AggregateException". The exception message shows us that "One or more errors occurred", and it also concatenates the inner exception messages.
Also note that there is an "InnerException" property that has the "InvalidOperationException". This is the exception that is shown when used "await" above.
But an AggregateException also has an "InnerExceptions" property (which is a collection). If we iterate over this, we find both of our exceptions: the "InvalidOperationException" and the "NotImplementedException".
When we use a continuation to check a Task exception directly, we get more details than when we use "await". The good news is that we can iterate over these inner exceptions and pass them along to our logging system.
Changing the Order
I was wondering if the exception that "await" shows for this code is deterministic. And it turns out that it is.
As an experiment, I reversed the order of the task list before passing it to "WhenAll". Here's the code for the "await" block:
With the reverse in place, we get a different output:
The output shows us the other exception that was generated - the NotImplementedException for ID 5.
We can also reverse the task list before sending it to the continuation:
The output shows us a bit better what is happening:
By looking directly at the AggregateException, we can see that the "InnerException" is now the one related to ID 5 (which is shown when we "await" things above).
Looking at the "InnerExceptions" collection, we see that the order is reversed. The ID 5 exception is first, and the ID 2 exception is second.
So when it comes to awaiting "Task.WhenAll", it appears that the exception we ultimately get is based on the order of the Tasks that are passed to the "WhenAll" method. Fun, huh?
Is This Important?
So how much do we need to care about this? The typical answer I would give is "probably not much". Tasks are extremely powerful, but we often do not need that power. And that's why "await" exists.
"await" gives us the 95% scenario. A common scenario I have is that I need to call an asynchronous method, wait for it to finish, do something with the result, and handle exceptions. If I only need to wait for one method at a time, then "await" works perfectly for this.
When we need to step outside of this scenario, it's good to understand how to use Task more directly. By understanding the process of setting up continuations and unwrapping AggregateExceptions, we can take more control of the situation when we need it.
Often we only care that "an exception happened". This lets us know that our application is in a potentially unstable state, and we need to handle things accordingly. We do not care about all of the details of what went wrong, and "await" is all we need. Whether we need to dig deeper depends on the needs of the users and the application.
If you want more information about using Task and await, you can check out the articles and video series on my website: I'll Get Back to You: Task, Await, and Asynchronous Methods in C#.