Channel Directions in C#
When using channels as function parameters, you can specify if a channel is meant to only send or receive values. This specificity increases the type-safety of the program. In C#, we can achieve similar behavior using BlockingCollection<T>
with AddingCompleted
property and TryAdd
/TryTake
methods.
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
class ChannelDirections
{
// This Ping method only accepts a BlockingCollection for adding values.
// It would be a runtime error to try to take from this collection.
static void Ping(BlockingCollection<string> pings, string msg)
{
pings.TryAdd(msg);
}
// The Pong method accepts one BlockingCollection for taking (pings)
// and a second for adding (pongs).
static void Pong(BlockingCollection<string> pings, BlockingCollection<string> pongs)
{
string msg;
if (pings.TryTake(out msg, TimeSpan.FromSeconds(1)))
{
pongs.TryAdd(msg);
}
}
static void Main()
{
var pings = new BlockingCollection<string>(1);
var pongs = new BlockingCollection<string>(1);
Ping(pings, "passed message");
Pong(pings, pongs);
string result;
if (pongs.TryTake(out result, TimeSpan.FromSeconds(1)))
{
Console.WriteLine(result);
}
pings.CompleteAdding();
pongs.CompleteAdding();
}
}
To run the program, compile and execute it:
$ csc ChannelDirections.cs
$ mono ChannelDirections.exe
passed message
In this C# version, we use BlockingCollection<T>
to mimic the behavior of Go channels. The Ping
method only adds to the collection, while the Pong
method takes from one collection and adds to another. This approach provides a similar level of direction specificity as in the original Go code.
The Main
method sets up the collections, calls the methods, and then prints the result. We use TryTake
with a timeout to avoid indefinite blocking, and we call CompleteAdding
on the collections when we’re done with them, which is similar to closing a channel in Go.
While this C# code provides similar functionality to the Go example, it’s worth noting that C# has other concurrency primitives that might be more idiomatic in certain scenarios, such as Task<T>
and async/await
.