How to pretend a class implements an interface in C#

C# interfaces are great, and since C# 8.0 they’re even cooler than they were before. Personally, I find them great for unit testing since I can use dependency injection to have my code reference a vital object by its interface, then pass in either a real or simulated version of that object depending on whether this is a unit test or real use. But how are we supposed to simulate a class that doesn’t implement an interface? I have just the trick!

Ideal simulation situation (no tricks needed)

The code below is an example of the ideal situation where you’re using a class Library.Foo that already implements an interface Library.IFoo. Our application’s method MyAppCode.UseFoo.CallBar takes a Library.IFoo instance called usuallyFoo as input. During normal execution of the application usuallyFoo will be an instance of Library.Foo, but during unit testing we’ll pass in an instance of our custom MyTestCode.SimFoo class instead.

namespace Library
{
    public interface IFoo
    {
        void Bar();
    }

    public class Foo : IFoo
    {
        public void Bar()
        {
            // library implementation
        }
    }
}

namespace MyAppCode
{
    public static class UseFoo
    {
        public static void CallBar(Library.IFoo usuallyFoo)
        {
            usuallyFoo.Bar();
        }
    }
}

namespace MyTestCode
{
    class SimFoo : Library.IFoo
    {
        public void Bar()
        {
            // simulated implementation
        }
    }

    static class TestUseFoo
    {
        public static void TestCallBar()
        {
            MyAppCode.UseFoo.CallBar(new SimFoo());
        }
    }
}

Time to pretend

I discovered this trick because my current job involves the System.IO.Ports.SerialPort class a lot, and that class does not implement an interface conducive for testing. Rather than a contrived abstract example with more Foo and Bar, let’s base this section on the real SerialPort class.

First, we need to know what members of the SerialPort class our application is using. If our code looks like the code below, then we need three members: IsOpen, Open(), and ReadLine().

using System.IO.Ports;

namespace MyAppCode
{
    public static class MyClass
    {
        public static string ReadFromSerialPort(SerialPort serialPort)
        {
            if (!serialPort.IsOpen)
                serialPort.Open();

            return serialPort.ReadLine();
        }
    }
}

Refactor the code above by making an interface MyAppCode.ISerialPort that declares the three members of System.IO.Ports.SerialPort that we need.

namespace MyAppCode
{
    public interface ISerialPort
    {
        bool IsOpen { get; }
        void Open();
        string ReadLine();
    }

    public static class MyClass
    {
        public static string ReadFromSerialPort(ISerialPort serialPort)
        {
            if (!serialPort.IsOpen)
                serialPort.Open();

            return serialPort.ReadLine();
        }
    }
}

Now time for the trick. We’ll make a new class MyAppCode.MySerialPort that inherits from System.IO.Ports.SerialPort and implements MyAppCode.ISerialPort.

using System.IO.Ports;

namespace MyAppCode
{
    public class MySerialPort : SerialPort, ISerialPort
    {

    }
}

That’s all we need! MyAppCode.MySerialPort doesn’t need any code to implement MyAppCode.ISerialPort because its base class already has members with the signature of each member of MyAppCode.ISerialPort.

We can now make and use a simulated serial port like so:

using System;

namespace MyTestCode
{
    public class SimSerialPort : MyAppCode.ISerialPort
    {
        public bool IsOpen { get; private set; } = false;

        public void Open() => IsOpen = true;

        public string ReadLine()
        {
            if (!IsOpen)
                throw new InvalidOperationException();

            return "Hello simulated world!";
        }
    }

    static class TestMyClass
    {
        public static void TestReadFromSerialPort()
        {
            MyAppCode.MyClass.ReadFromSerialPort(new SimSerialPort());
        }
    }
}

Remember to /// <inheritdoc/>

There’s one last change to make. Since in our code we’ll now be referencing MyAppCode.ISerialPort instead of System.IO.Ports.SerialPort we’re not going to see all the great documentation we’re used to in Visual Studio when we mouse over members like Open() and ReadLine(). Not to worry, though, there’s a very simple fix. Just add /// <inheritdoc cref="SerialPort.X"/> to each of ISerialPort‘s members, where X is the member name. Here’s our final interface:

using System.IO.Ports;

namespace MyAppCode
{
    public interface ISerialPort
    {
        /// <inheritdoc cref="SerialPort.IsOpen"/>
        bool IsOpen { get; }

        /// <inheritdoc cref="SerialPort.Open"/>
        void Open();

        /// <inheritdoc cref="SerialPort.ReadLine"/>
        string ReadLine();
    }
}

And here’s a screenshot of how the documentation looks when we mouse over something:

Restricting a class

This trick mostly restricts a class to only those members that are declared in the interface that you create. This could be useful if the class contains members that you don’t want people to use for whatever reason. However, I say “mostly restricts” since the the full class can still be accessed via casting, like in the code below.

public static void Cast(ISerialPort serialPort)
{
    var cast = serialPort as SerialPort;
    // Close() is not a member of ISerialPort
    cast!.Close();
}

Sealed classes

Obviously, this trick won’t work on a class marked sealed since such a class cannot be inherited. However, you can still implement your interface through a sealed class by using composition like in the code below. The only downside is that you’ll probably get tired of writing all those lambdas.

using System.IO.Ports;

namespace MyAppCode
{
    class CompositionSerialPort : ISerialPort
    {
        readonly SerialPort _serialPort = new();

        public bool IsOpen => _serialPort.IsOpen;

        public void Open() => _serialPort.Open();

        public string ReadLine() => _serialPort.ReadLine();
    }
}

Conclusion

I’ll admit, this trick is far from groundbreaking, but I still think it’s cool. When I first tried this and found that it actually compiled I had a big sense of “What? No way!” since I had thought that I would need to explicitly define all of ISerialPort‘s members on MySerialPort.

Source code for these examples is freely available here.

Yet another explanation of variance in C#

Type variance is a somewhat difficult concept in programming, and one that you seldom need to actually know about, so I managed to ignore it for quite awhile until I finally decided that it was time to buckle down and understand it. There are many online guides, but the ones I read only got me so far. It wasn’t until I spent an afternoon playing with the code myself that everything clicked and the guides I had once scratched my head at finally made sense.

I had been missing a certain surface level understanding that I needed to be able to really read about the topic. This post won’t be a full in-depth explainer of the subject. Instead, I’ll try to get across the key points of what variance is and isn’t so that you’ll be able to use variance and grasp the deeper explanations.

Introduction

In C#, type variance (often just called “variance” in this context) is the ability or inability of an instance of a generic interface or generic delegate to be substituted with another whose type parameter is a more or less derived type.

Let’s break that down a bit:

  1. “a generic interface or generic delegate” – Variance only applies to generic interfaces and generic delegates. It does not apply to classes, structs, non-generic interfaces, or non-generic delegates.
  2. “to be substituted with another” – Variance only matters when assigning instances of the generic type to variables of the generic type or when they are passed as method parameters. This point will make more sense after the examples.
  3. “ability or inability” and “whose type parameter is a more or less derived type” – There are 3 types of variance in C#: covariance (can use a more derived type), contravariance (can use a less derived type), and invariance (must use the same type).

Type parameters are invariant by default. They are made covariant with the out keyword and contravariant with the in keyword.

Setup

We’ll use the Func<TOutput> and Action<TInput> delegates for our examples since they’re simple and familiar delegates that will let us see both co- and contravariance. These are their signatures. Note the out keyword (covariant) on TOutput and the in keyword (contravariant) on TInput.

delegate TOutput Func<out TOutput>();

delegate void Action<in TInput>(TInput input);

We’ll need two dummy classes for our examples. They are empty, since all that matters is that Derived inherits from Base. We will also need two methods: one that takes no input and returns a Derived, and one that takes a Base as input and returns nothing. The method bodies do not matter, only their signatures.

class Base { }

class Derived : Base { }


Derived ReturnDerived() { return new Derived(); }

void InputBase(Base input) { }

Lastly, let’s make worse versions of those Func and Action delegates. We’ll call them InvariantFunc and InvariantAction, and they’ll have the same signatures as Func and Action but without the out and in keywords. As their names imply, this will make them invariant.

delegate TOutput InvariantFunc<TOutput>();

delegate void InvariantAction<TInput>(TInput input);

Invariance and ordinary polymorphism

A big mistake I made when learning about variance is that I mistook ordinary polymorphism for co- and contravariance. To prevent you from making the same mistake, let’s take a look at what we can do with our invariant delegates:

void LegalInvariance()
{
    InvariantFunc<Base> notCovariance = ReturnDerived;
    Base aBaseClassInstance = notCovariance();

    InvariantAction<Derived> notContravariance = InputBase;
    notContravariance(new Derived());
}

This is all legal since it doesn’t need covariance or contravariance. This is just regular polymorphism.

Why this is ordinary polymorphism

A method that returns Derived and takes no input can be assigned to notCovariance even though notCovariance is an InvariantFunc<Base>. This is because every instance of Derived is also an instance of Base, so when notCovariance() is called, it returns a valid Base to assign to aBaseClassInstance.

Similarly, a method that returns nothing and takes an instance of Base as input can be assigned to notContravariance even though notContravariance is an InvariantAction<Derived>. Again, this is because every instance of Derived is also an instance of Base, so when notContravariance(new Derived()) is called, the InputBase method is being passed a valid instance of Base.

Illegal invariance

This code is not legal, even though it essentially does the same thing as lines 3 and 6 of the LegalInvariance method:

void IllegalInvariance()
{
    InvariantFunc<Derived> returnDerived = ReturnDerived;
    // illegal:
    InvariantFunc<Base> needsCovariance = returnDerived;


    InvariantAction<Base> inputBase = InputBase;
    // illegal:
    InvariantAction<Derived> needsContravariance = inputBase;
}

Just like before we’re assigning ReturnDerived to an InvariantFunc<Base> variable and assigning InputBase to an InvariantAction<Derived> variable, so why is it illegal this time around?

Recall point #2 in the introduction: “variance only matters when assigning instances of the generic type to variables of the generic type….” Line 5 attempts to assign an instance of InvariantFunc<Derived> to an InvariantFunc<Base> variable, which requires covariance. Line 10 attempts to assign an instance of InvariantAction<Base> to an InvariantAction<Derived> variable, which requires contravariance.

Covariance and contravariance

So, after all that talk about invariance, let’s see covariance and contravariance in action:

void CovarianceAndContravariance()
{
    Func<Derived> returnDerived = ReturnDerived;
    // legal because TOutput in Func<TOutput> is covariant
    Func<Base> usesCovariance = returnDerived;


    Action<Base> inputBase = InputBase;
    // legal because TInput in Action<TInput> is contravariant
    Action<Derived> usesContravariance = inputBase;
}

This is the same as the IllegalInvariance method but using Func and Action instead of InvariantFunc and InvariantAction. This code is now legal.

This is of course a trivial example of covariance and contravariance, but it gets the idea across.

What kind of variance should you use for your generic types?

So when should we make our type parameters covariant, contravariant, or invariant? Here are some rules:

  1. Recall point #1 in the introduction: “variance only applies to generic interfaces and generic delegates”. So if we’re not defining a generic interface or a generic delegate then it’s irrelevant.
  2. If a type parameter is used as any of the following then it must be invariant:
    • ref return
    • ref argument
    • in argument
    • out argument
  3. If a type parameter is only used as output (method/delegate returns and get properties) and does not violate rule #2, then it can be covariant. Remember: “out for output.”
  4. If a type parameter is only used as input (method/delegate argument and set properties) and does not violate rule #2, then it can be contravariant. Remember: “in for input.”
  5. If a type parameter is used for both input and output then it must be invariant.

Co- and contravariance make code more flexible, so if we can make a type parameter co- or contravariant we should do it unless we have a reason not to. Now that you’re aware of them, you’ll probably start noticing in and out on type parameters all over .NET.

Conclusion and resources

I hope you came away with a better understanding of variance in C#. Leave a comment if this helped, if there are any errors I should correct, or if you’ve got a question.

Source code for these examples is freely available here.

For further reading, I recommend Vasil Kosturski’s blog post on the subject.

The Microsoft documentation is also very informative, but it’s rather dense and strangely spread across different parts of their documentation website. Here are two good entry points to it:

Lastly, if you’re interested in the theory of type variance, check out the Wikipedia entry.

My misconceptions about the C# lock keyword

This is embarrassing, but for quite awhile I had been using C#’s lock keyword wrong. This is how I had thought that it worked:

When an object Foo is locked by thread A, none of Foo’s members can be changed in another thread B without blocking B until the lock is released.

Most C# devs probably read that and either chuckled or cringed. That’s an understandable reaction, because, once again, it is very wrong. However, I’m probably not the only one to have been confused by lock, hence this blog post. This is how it actually works:

When an object Foo is locked by thread A and another thread B also tries to lock Foo, then thread B will be blocked until the lock is released by thread A.

There is a world of difference between those two! lock is much less powerful than I previously thought. Here are some important notes about it and what it doesn’t do:

  • A locked object can have its members changed from another thread.
  • If an object is locked by one thread and one of the object’s members is locked by another thread, then neither thread will be blocked since the locks are on different objects.
  • Locks are taken on objects, not variables. So if an object Foo is locked by thread A and then Foo is reassigned to a new object, then when thread B locks Foo, B will not be blocked by A’s lock.

Let’s see some examples to illustrate, especially that last point.

Here are 2 classes. InnerClass just has a public int that multiple threads will increment. OuterClass has a public instance of InnerClass and a public method that we’ll call from multiple threads with different lock behavior. The method loops 20 times to increment Inner‘s field and write the new value as well as the name of the current thread to the console. A 1 millisecond sleep ensures that the method doesn’t run so fast that the threads don’t have time to interfere with one another.

class InnerClass
{
    public int FieldThatAllTheThreadsWant = 0;
}

class OuterClass
{
    public InnerClass Inner = new InnerClass();

    public void ChangeFieldAndWriteToTheConsole()
    {
        for (int i = 0; i < 20; ++i)
        {
            ++Inner.FieldThatAllTheThreadsWant;
            Console.WriteLine("{0}; field = {1}", 
                Thread.CurrentThread.Name, 
                Inner.FieldThatAllTheThreadsWant);

            Thread.Sleep(1);
        }
    }
}

We’ll make a static instance of OuterClass called Outer and call its method from these three methods that lock on Outer, Outer.Inner, or nothing.

static OuterClass Outer = new OuterClass();

static void LockOnOuter()
{
    lock (Outer)
    {
        Outer.ChangeFieldAndWriteToTheConsole();
    }
}

static void LockOnInner()
{
    lock (Outer.Inner)
    {
        Outer.ChangeFieldAndWriteToTheConsole();
    }
}

static void DontLock()
{
    Outer.ChangeFieldAndWriteToTheConsole();
}

Here’s a method that shows the wrong way to use lock. It makes three threads that each call a different one of the three methods we made above, then writes the final value of the field to the console. What I would hope to see based on my earlier misunderstandings of lock is for each thread to write 20 consecutive lines to the console and for the final value to be 60. Run the code and each thread’s 20 lines are anything but consecutive. That should be no surprise to anyone reading this, but what may surprise some is that the final value will not always be 60!

static void WrongWay()
{
    var t1 = new Thread(LockOnOuter)
    {
        Name = "T1"
    };

    var t2 = new Thread(LockOnInner)
    {
        Name = "T2"
    };

    var t3 = new Thread(DontLock)
    {
        Name = "T3"
    };

    t1.Start();
    t2.Start();
    t3.Start();

    t1.Join();
    t2.Join();
    t3.Join();

    Console.WriteLine("Final value: {0}", Outer.Inner.FieldThatAllTheThreadsWant);
}

Per the documentation it turns out that while reading and writing primitive types like int is always atomic, “there is no guarantee of atomic read-modify-write, such as in the case of increment or decrement”. If we wanted to guarantee in the above code that the result will always be 60, we could use the Increment method from the Interlocked static class.

So we’ve seen how not to use lock. This is how it should be used. Multiple threads locking on the same object, in this case on Outer. Run it and the two threads will have their messages appear consecutively and the final value will always be 40.

static void RightWayOuter()
{
    var t1 = new Thread(LockOnOuter)
    {
        Name = "T1"
    };

    var t2 = new Thread(LockOnOuter)
    {
        Name = "T2"
    };

    t1.Start();
    t2.Start();

    t1.Join();
    t2.Join();
    Console.WriteLine("Final value: {0}", Outer.Inner.FieldThatAllTheThreadsWant);
}

Of course, we could also lock on Outer.Inner instead of Outer. That’s shown below for completeness. Since both threads lock on the same object, the result is the same as above.

static void RightWayInner()
{
    var t1 = new Thread(LockOnInner)
    {
        Name = "T1"
    };

    var t2 = new Thread(LockOnInner)
    {
        Name = "T2"
    };

    t1.Start();
    t2.Start();

    t1.Join();
    t2.Join();

    Console.WriteLine("Final value: {0}", Outer.Inner.FieldThatAllTheThreadsWant);
}

Lastly, I’d like to point out a subtlety of lock that I had never considered before I started this post. A lock is taken on an object, not on a variable. That sentence probably makes perfect sense when read, but it means we have to be careful about reassigning a variable being locked.

To illustrate, take a look at the method below. It’s identical to RightWayOuter, except that between the two threads starting we reassign Outer. This means that the two threads lock on different objects even though they lock on the same variable. Run the code and see that the threads’ messages are not all consecutive and the final is 20, not 40.

static void WrongWayChangingVariable()
{
    var t1 = new Thread(LockOnOuter)
    {
        Name = "T1"
    };

    var t2 = new Thread(LockOnOuter)
    {
        Name = "T2"
    };

    t1.Start();

    // sleep to ensure that t1 has actually started before reassigning
    Thread.Sleep(10);
    Outer = new OuterClass();

    t2.Start();

    t1.Join();
    t2.Join();

    Console.WriteLine("Final value: {0}", Outer.Inner.FieldThatAllTheThreadsWant);
}

I hope you came away with a better understanding of lock or that you at least got to laugh at my old mistakes. Leave a comment if this helped, if there are any errors I should correct, if you’ve got a question, or if you want to share your own embarrassing misconceptions. Source code for this example is freely available here.