Writing Pure Code in C#

Posted by: Yacoub Massad , on 10/14/2018, in Category Patterns & Practices
Views: 31173
Abstract: In this article, I will work on a C# game that contains impure code and work towards making the code pure. I am going to show you how to use PurityAnalyzer—an experimental Visual Studio extension I wrote—to help you write pure code in C#.

Pure Methods and Classes in C#

Functional programming has been gaining popularity. Languages that started as object-oriented have been getting functional programming features. C# for example, has been getting many features related to functional programming.

In the Functional Programming for C# Developers article, Damir Arh talked about some functional features of C#. In the same article, he also talked about pure functions.

This tutorial is from the DotNetCurry(DNC) Magazine with in-depth tutorials and best practices in .NET and JavaScript. This magazine is aimed at Developers, Architects and Technical Managers and covers C#, Patterns, .NET Core, MVC, Azure, DevOps, ALM, TypeScript, Angular, React, and more. Subscribe to this magazine for FREE and receive all previous, current and upcoming editions, right in your Inbox. No Spam Policy.

A pure function is a function whose output depends solely on the arguments passed to it. If we invoke a pure function twice using the same input values, we are guaranteed to get the same output. Also, a pure function has no side effects.

All of this means that a pure function cannot mutate a parameter, mutate or read global state, read a file, write to a file, etc. Also, a pure function cannot call another function that is impure.

In the same article, the author also mentions that in C#, developers are required to have some discipline to write pure code. This is true because in C#, it is easy to mutate a parameter, get or set the value of a static field, read from or write to a file, etc.

The Haskell programming language has a different story altogether.

In Haskell, functions are pure by default and values are immutable by default. This means that writing pure functions in Haskell requires much less discipline (if any) than doing so in C#.

To help myself and other developers write and maintain pure code in C#, I have started working on a Visual Studio extension called PurityAnalyzer. Using this extension, developers can mark certain code as pure, and the analyzer would check the purity of the code and generate errors if the code is not pure.

In this article, I will work on an existing program that contains impure code. My target is to make the code pure. I will use the PurityAnalyzer extension to help me do so.

Please note that my focus in this article is on code purity. In the examples I discuss, I am not going to worry about other coding practices such as testing, good naming.

The Tic-tac-toe game

In this article, I will walkthrough a tic-tac-toe game written in C#. I am going to assume that the reader is already familiar with this game. If you are not, please read about it here: https://en.wikipedia.org/wiki/Tic-tac-toe

Look at the first version of the game code here: https://github.com/ymassad/PureCodeInCSharpExample/tree/FirstImpureVersion

I also suggest that you run the game and see how it is played. The TicTacToeGame project has the following classes:

  • The Board class represents the 9-cells board of the game. Internally it stores the cell contents as a jagged array of an Enum called CellStatus. This Enum could be one of three: Empty, HasX, or HasO. This class contains methods to get and set the contents of a cell given its row and column. It also contains a PrintToConsole method that prints the contents of the board to the console in a formatted way. It also contains an IsFull method that can determine whether all cells have been played. It also contains a property called Winner which is of type Player? (Nullable<Player>). The caller can use this property to check who has won the game; player X or O, if any.
  • The Game class controls the whole game. The PlayGame method starts a game session. It creates a new Board object and then allows each player to play until the game is over, either when there is a winner or when the board is full. This method calls the PlayOneTurn method multiple types. Each call will ask the current player to select a cell (represented by a row and a column) to play.

The PlayMultipleTimes method keeps calling the PlayGame method until the user decides to exit.

The Main method simply creates a new Game object and invokes the PlayMultipleTimes method.

Is the code in these classes pure? Let’s find out.

If you like to follow the steps I describe here, please install the PurityAnalyzer extension for Visual Studio 2017 from: https://marketplace.visualstudio.com/items?itemName=yacoubmassad.PurityAnalyzer

Making the Game class pure

Open the Game.cs file and annotate the class with the IsPure attribute like this:

pure-attribute-on-game-class

Figure 1: Annotating the Game class with the IsPure attribute

Because the IsPure attribute is not defined in code, Visual Studio shows a red squiggle underneath the annotation.

Visual Studio can help with generating the attribute. If you hover over the annotation, Visual Studio will show the Roslyn light bulb. You can now choose to generate the attribute.

Here is the definition of the IsPure  attribute if you want to add it manually:

public class IsPureAttribute : Attribute
{
}

By annotating the class with the IsPure attribute, we claim that all methods in the class are pure. Now, the PurityAnalyzer extension will analyze the class methods and generate errors for any code that makes the methods impure.

impure-code

Figure 2: Errors generated because of impure code

Figure 2 shows part of the Game class. In particular, it shows the PlayMultipleTimes method. The Console.WriteLine method is impure because it writes to the console. Also, accessing the numberOfTimesXWon and the numberOfTimesOWon fields makes the method impure because these fields are mutable (non-readonly).

I will talk more about mutable fields later. For now, let’s talk about Console.WriteLine.

Many methods in the class use Console.WriteLine. To work towards making these methods pure, we need to get rid of Console.WriteLine.

In a previous article, Writing Honest Methods in C#, I talked about making impure methods potentially-pure by replacing direct invocations of impure functions with invocations of functions passed as parameters. Let’s apply this here.

Because we are working with instance methods, we can either have the function parameters added as method parameters, or we can add them as constructor parameters. For now, I will use the second option.

Here is how the class looks like now:

extract-write-console-function

Figure 3: Extracting a writeToConsole function as a constructor parameter

Figure 3 shows how the Game class now takes a writeToConsole function as a parameter and how the PlayMultipleTimes method calls it.

I have also changed other methods in the Game class to use the writeToConsole parameter instead of directly calling Console.WriteLine.

Notice how there are no red squiggles underneath the invocation of the writeToConsole function. PurityAnalyzer assumes that calls to delegates are always pure. It is the responsibility of the caller to pass a pure function if it wants to be pure.

Now, we have to go to the Main method and supply a value for the writeToConsole parameter like this:

static void Main(string[] args)
{
    new Game(Console.WriteLine).PlayMultipleTimes();
}

Of course, the Main method is not pure. It passes an impure function (Console.WriteLine) to the constructor of the Game class. But now, the impure dependencies of the Game class are no longer hidden. For more information about this, see the Writing Honest Methods in C# article.

To see how the code looks like now, take a look here:

https://github.com/ymassad/PureCodeInCSharpExample/tree/ExtractingWriteToConsole

Some methods in the class, e.g. the PlayAgain method, invoke the Console.ReadLine method to read a line from the console. Let’s create a readFromConsole function parameter like we did for Console.WriteLine.

To see how the code looks like now, look here:

https://github.com/ymassad/PureCodeInCSharpExample/tree/ExtractingReadFromConsole

Let’s work on the other impure method invocations. The ReadRowAndColumnFromConsole method invokes the int.TryParse method. You should see red squiggles underneath such invocation because the int.TryParse method is impure.

But why is it impure?

Consider the following code:

var result1 = int.TryParse("+1000", out var parsed1);

Thread.CurrentThread.CurrentCulture = (CultureInfo)Thread.CurrentThread.CurrentCulture.Clone();

Thread.CurrentThread.CurrentCulture.NumberFormat.PositiveSign = "p";

var result2 = int.TryParse("+1000", out var parsed2); 

This code invokes the TryParse method two times. It passes the same value (“+1000”) in both invocations. If you run this code however, the first attempt to parse will be a success (result1 will be true), but the second one will be a failure (result2 will be false).

The reason the second one fails is that between the two calls, we modify the NumberFormat.PositiveSign property of the current CultureInfo object. For information about this topic, see the documentation of the CultureInfo class in the .NET Framework.

What is important to note here is that the behavior of the TryParse method does not depend solely on the arguments passed to it. Internally, it reads the Thread.CurrentThread.CurrentCulture property (which is the global state) to obtain number formatting settings that will make some decisions related to parsing.

There is another overload of the TryParse method that takes in an IFormatProvider parameter (implemented by NumberFormatInfo and CultureInfo for example) that we can use to specify the number formatting settings we want to use when parsing.

bool TryParse(string s, NumberStyles style, IFormatProvider provider, out int result)

The problem with such overload though is that we can pass null as an argument for this parameter which will make the method fall back to using the current culture’s number formatting settings. So, we cannot assume that this overload is pure.

Understandably, the .NET framework was not built with purity in mind.

What to do now? Should we extract the TryParse method as a function parameter like we did with Console.WriteLine and Console.ReadLine?

While we can do that, I don’t think this is a good idea in the context of the TicTacToe game.

In our game, we simply need TryParse to parse the column and row numbers that the user entered. This is pure logic and we shouldn’t have to extract it.

There are three rows and three columns so we need to parse only the following strings: “1”, “2”, “3”.

We can create a simple method to do that like this:

static bool TryParse1Or2Or3(string str, out int result)
{
    if (str == "1") { result = 1; return true; }

    if (str == "2") { result = 2; return true; }

    if (str == "3") { result = 3; return true; }

    result = 0;
    return false;
}

...or create our own TryParse method from scratch if we want to.

The first solution might be acceptable in this case because we only need to handle a few cases. The second solution includes writing large amounts of code which is not a very good thing.

Had the second overload of TryParse been designed in a way to reject a null argument for the IFormatProvider parameter, we would have considered this method to be pure and used it here.

The PurityAnalyzer extension is not smart enough to know that the second overload of TryParse is pure if the argument passed for the IFormatProvider parameter is not null.

To overcome these issues, let’s create the following methods:

public static class PureInt
{
    [AssumeIsPure]
    public static bool TryParse(
        this string s,
        NumberStyles style,
        IFormatProvider provider,
        out int result)
    {
        if (provider == null)
            throw new Exception($"{nameof(provider)} cannot be null");

        return int.TryParse(s, style, provider, out result);
    }

    public static bool TryParseCultureInvariant(
        this string s,
        out int result)
    {
        return TryParse(s, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out result);
    }
}

The PureInt.TryParse method simply invokes the second overload of the int.TryParse method I talked about. It also checks if the IFormatProvider argument is null and throws an exception if this is the case. This means that we can now assume that this method is pure.

This method is annotated with a special attribute, the AssumeIsPure attribute, to tell PurityAnalyzer that even if it cannot infer that the method is pure, it is actually pure.

The TryParseCultureInvariant method invokes the TryParse method passing NumberFormatInfo.InvariantInfo as the argument for the IFormatProvider parameter. This makes the method use number formatting settings that are culture-independent. See the documentation for this property for more information.

I changed the ReadRowAndColumnFromConsole method to call the PureInt.TryParseCultureInvariant method instead of int.TryParse.

To see how the code looks like now, look here:

https://github.com/ymassad/PureCodeInCSharpExample/tree/CreatingPureIntTryParse

Note: another solution is to consider the int.TryParse method pure enough and tell PurityAnayzer to consider it pure via the options page. See the last section of this article for more details.

Let’s continue working on the Game class. Inside the PlayBoard method, PurityAnalyzer is telling us that the PrintToConsole method in the Board class is impure.

We should expect this because this method writes to the console. If we go to the implementation of the method, we can see it invokes Console.WriteLine.

Let’s fix it really quick.

Let’s add a writeToConsole function parameter to this method and use it there. The PlayBoard method can easily pass the value in the writeToConsole field as an argument to this method.

To see how the code looks like now, take a look here:

https://github.com/ymassad/PureCodeInCSharpExample/tree/InjectingWriteToConsoleInPrintToConsole

Let’s work on the next issue. In the PlayMultipleTimes method of the Game class, two mutable static fields are read: numberOfTimesXWon and numberOfTimesOWon.

Reading mutable fields makes a method impure because when the method is called more than once, the values of such fields are not guaranteed to hold the same values. Since the method output might depend on such values, such output might be different on each invocation, even if we use the same input values each time.

“But wait a minute”, you might say. “The PlayMultipleTimes method returns void. So, the output of the method would be the same even if we call it multiple times. And even if the values of the static fields are different each time.”

Although the PlayMultipleTimes method returns void, it has some output; an indirect output. This method has indirect output through the writeToConsole constructor parameter (stored inside a field). The method invokes such function parameter to output the number of times each player won so far. For more information about direct and indirect output, see the Dependency Rejection blog post by Mark Seemann.

I hope that you see how such indirect output might be different if we invoked the PlayMultipleTimes method twice.

How to make the PlayMultipleTimes method pure? By making it not read global state.

Who updates this global state?

If we right click on the fields inside Visual Studio and select Find All References, we can see that these fields are updated inside the SetCell method of the Board class. Basically, when the SetCell method is invoked, it checks if there is a winner and increments one of the two static fields if any.

The following figure shows how the PlayMultipleTimes method calls the SetCell method indirectly:

set-cell-call-path

Figure 4: The call path from PlayMultipleTimes to SetCell

There are many ways to fix the issue at hand. I plan to do the following:

1. Make the SetCell method not update the static fields.

2. Make the PlayGame method return Player? instead of void to indicate to the caller (the PlayMultipleTimes method in this case) which player won the game, if any. The PlayGame method can obtain such information from the Winner property of the Board object that is available in local scope.

3. Replace the static fields with local variables inside the PlayMultipleTimes method. The PlayMultipleTimes method will update such variables based on the output it receives from the PlayGame method (of type Player?).

Here is how the PlayMultipleTimes method looks like now:

remove-mutable-static-fields

Figure 5: The PlayMultipleTimes method after replacing the fields with local variables

We still have state. numberOfTimesXWon and numberOfTimesOWon are mutable local variables. Their values might change on each iteration of the loop. What we have done is reduce the scope of the state from global to local.

To see how the code looks like now, look here:

https://github.com/ymassad/PureCodeInCSharpExample/tree/RemovingStaticFields

Since we fixed the issue with accessing the static fields, there are no longer any red squiggles inside the PlayMultipleTimes method. This means that the PlayMultipleTimes method is pure. Or is it?

Actually, the PlayMultipleTimes method is still not pure.

The PlayMultipleTimes method calls the PlayBoard method indirectly, via the PlayGame and the PlayOneTurn methods. The PlayBoard method is still impure as evident by the red squiggles underneath the call to SetCell.

Why are there no red squiggles underneath the invocation of PlayGame inside the PlayMultipleTimes method then?

When PurityAnalyzer analyzes the PlayMultipleTimes method and finds an invocation of the PlayGame method, it checks whether the PlayGame method or its containing type (the Game class) are marked as pure.

Since the Game class is marked with this attribute, PurityAnalyzer does not analyze the code of PlayGame at this moment and assumes that it is pure.

Of course, the PlayGame method will be analyzed on its own after the analyzer is done with the PlayMultipleTimes method. But again, when the analyzer sees the call to PlayOneTurn, it will see that the containing class is marked as pure and will not check the purity of the PlayOneTurnmethod.

This is not a problem because PurityAnalyzer is still detecting that the PlayBoard method is impure. Once we fix this, PurityAnalyzer’s findings about PlayMultipleTimes , PlayGame , PlayOneTurn will be correct.

This is all done for performance reasons. Without such a way of analyzing, PurityAnalyzer will end up analyzing the same method multiple times.

If we remove the IsPure annotation on the Game class and just annotate the PlayMultipleTimes method with this attribute, PurityAnalyzer will show red squiggles underneath the call to PlayGame inside PlayMultipleTimes . This is because it will be forced to analyze the methods all the way down.

Making the Board class pure

We are now left with the impurity inside the PlayBoard method. PurityAnalyzer doesn’t like the call to SetCell here. Let’s go to the SetCell method and mark it as pure to see what’s wrong with it.

set-cell-annotated-pure-attribute

Figure 6: PurityAnalyzer complains about reading and writing mutable state in the SetCell method

Let’s start with the access to the lines field.

Look at the definition of this field (line 56). The type of this field is (int row, int column)[][]. This is an array of lines on the board players can fill to win. This includes three horizontal lines, three vertical lines, and two diagonal lines (8 in total).

Each line is an array of cell locations. Each cell location is represented by a tuple containing row and column indexes.

If we hover over the lines field access inside the SetCell method, we would see that PurityAnalyzer is objecting to the fact that we are accessing a mutable field. There is no need for this field to be mutable since the information it contains need not change.

Let’s make this field read-only.

Still, there are red squiggles underneath where it is accessed inside the SetCell method. However, when we hover over it again, we see a different error. PurityAnalyzer is now talking about an impure cast.

“What?”

What the code is doing is calling the Enumerable.Any method, as an extension method over the lines field. Here is the signature of this method:

public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);

The first parameter of this method is of type IEnumerable<TSource>. This means that when we call the Any method, the lines field which is of type array needs to be cast to IEnumberable<TSource>.

PurityAnalyzer is designed to assume that all abstract methods, e.g. interface methods, are pure. This allows us to create potentially-pure methods/classes by having them take parameters whose types are interfaces, not just delegates (Func and Action for example).

For more information about creating potentially-pure methods, see the Writing Honest Methods in C# article.

When casting from some type to IEnumerable<T>, PurityAnalyzer checks to see if the GetEnumerator method on that type is pure. If it is not, then PurityAnalyzer rejects the cast.

It does so because we shouldn’t be able to trick PurityAnalyzer into accepting a call to an impure GetEnumerator method by first casting the object to IEnumerable<T> and then invoking the GetEnumerator method on that.

The GetEnumerator method on the array type is not pure because the array is mutable and invoking GetEnumerator twice might yield different results if the array contents change between the calls.

To think more concretely, think about the fact that even though the lines field is read-only, we can still mutate individual elements inside the array. For example, we can do this:

lines[0][0] = (5, 5);

Since the SetCell method depends on the lines field contents, its output (which is currently the new state of the current Board object) is not guaranteed to be the same on different calls, even if we pass the same input values.

To fix this problem, let’s change the type of the field to use immutable arrays instead of mutable ones.

immutable-array-lines

Figure 7: An excerpt from the updated lines field which is now an immutable array of immutable arrays

Now, the red squiggles underneath where we access the lines field inside the SetCell method disappeared.

The Any method that we are calling now is not Enumerable.Any. It is ImmutableArrayExtensions.Any. It has the following signature:

public static bool Any<T>(this ImmutableArray<T> immutableArray, Func<T, bool> predicate)

This extension method acts directly on the ImmutableArray<T> type and therefore does not require a cast to IEnumerable<T>. This method is potentially-pure. If we give it the same immutable array, and the same pure predicate, the output will always be the same.

But even if we cast the lines field to IEnumerabe<T>, PurityAnalyzer will not object. The cast will be “pure”. You can test this by writing the following line:

var enumerableLines = lines.AsEnumerable();

You will see no red squiggles here. However, if you change the type of the lines field back to an array, you will get the same impure cast error from before.

To see how the code looks like now, take a look here:

https://github.com/ymassad/PureCodeInCSharpExample/tree/MakingLinesArrayImmutable

Let’s continue working on the other errors generated by PurityAnalyzer. There are three errors remaining:

1. Mutating the Cells array contents (instance property)

2. Reading the mutable Cells array contents (the same instance property)

3. Mutating the Winner instance property. Currently, when SetCell is called, we determine if there is a winner and we set the Winner property if so.

I am going to discuss two solutions.

Solution 1: Making the Board class immutable

The first solution is to make the Board class immutable. Currently, the Board class is mutable. When we invoke the SetCell method, two fields might be mutated. Here are the changes that I will make:

1. Change the type of the Cells property from CellState[][] to ImmutableArray< ImmutableArray<CellState>>. This means that once a Board object is created, its cell contents cannot be modified.

2. Make the SetCell method a static method that takes a Board object and returns another Board object that has the specified cell updated.

3. Create an additional constructor in the Board class that accepts an ImmutableArray< ImmutableArray<CellState>> array to initialize the Cells property. This constructor will be used by the SetCell method to create a Board object with the modified CellState values.

4. Remove the Winner property and instead create a static GetWinner method that the caller can use to determine if there is a winner based on the data in the Cells property. I chose to make this method static, but it could also be an instance method.

5. Update the PlayBoard and PlayOneTurn methods in the Game class to return the new updated Board object.

6. Update the PlayGame method to keep track of the current Board object in the board variable. Mutating the board variable keeps the method pure because it is state that is local to the method.

7. In the PlayGame method, call GetWinner on the board object after each call to PlayOneTurn to see if there is a winner.

PurityAnalyzer now generates no errors.

To see how the code looks like now, look here:

https://github.com/ymassad/PureCodeInCSharpExample/tree/MakingBoardClassImmutable

Solution 2: Living with a mutable Board class

While the first solution is a good one for our particular game, it might not be acceptable in other programs in terms of performance.

In the first solution, each time we want to modify a single cell, we create two new immutable arrays. Let me explain.

The following figure represents the Cells property in the Board class. The red box represents the outer array, which is an array of arrays. The inner three blue rectangles represent the three arrays that are inside the outer array.

When we set a cell via SetCell, we create a new outer array, and then we take two of the inner arrays as-is and put them in the new outer array. We then create another array that contains the same values of the other inner array, except for the cell that we want to modify. We then put this new array in the new outer array.

create-new-immutable-arrays

Figure 8: The outer array (in red), and the inner arrays (in blue)

The TicTacToe game is an interactive game and we expect the players to play one turn every second or so. So, the effect of creating the new arrays is negligible.

Imagine however that you are creating an application that needs to update one element in a 1000 by 1000 array 10000 times per second. Now, the performance cost of immutable objects might be significant.

The performance of immutable objects is not as bad as it might seem at first. Because such objects are immutable, we can reuse parts of one object in another object, and therefore we don’t need to copy everything when we need to modify a little part of an immutable object.

Still, the performance cost of “modifying” an immutable object is larger than that of a mutable object.

Before we start working on the second solution, let’s revert the code changes made in the last section.

To see how the code looks like now, take a look here:

https://github.com/ymassad/PureCodeInCSharpExample/tree/RevertingToMutableBoardClass

Here is how the PlayBoard method looks like now:

error-related-set-cell

Figure 9: The PlayBoard method is not pure because it invokes the impure SetCell method

The figure above shows the errors generated by PurityAnalyzer regarding the call to SetCell. To remind you, PurityAnalyzer is not happy because SetCell mutates the state inside the board object.

How to fix this?

Let’s do the following changes:

1. Convert the PlayOneTurn method to a local function inside the PlayGame method. Remove the Board and Player parameters from such local function. Since PlayOneTurn is now inside the PlayGame method, it can access both the board and the currentPlayer local variables.

2. Convert the PlayBoard method to a local function inside the PlayGame method. Remove the Board and Player parameters from such local function. Since PlayBoard is now inside the PlayGame method, it can access both the board and the currentPlayer local variables.

3. Remove the arguments that are no longer needed when invoking PlayOneTurn and PlayBoard.

To see how the code looks like now, take a look here:

https://github.com/ymassad/PureCodeInCSharpExample/tree/PureExceptLocallyBoardClass

PurityAnalyzer no longer complains. But why? The SetCell method is still not pure!

PurityAnalyzer supports three levels of purity:

purity-analyzer

The SetCell method is pure-except-locally. The only thing preventing it from being pure is the fact that it reads and mutates instance state (the Cells and Winner properties).

In the PlayBoard local function inside the PlayGame method in the Game class, we are calling SetCell on the board variable. PurityAnalyzer detects that this variable contains a new object, and therefore allows us to call the SetCell method on such a variable.

But why does PurityAnalyzer allow us to call pure-except-locally methods on new objects?

Because the state of such objects is local to the specific invocation of the calling method and therefore cannot affect the output of the calling method on later invocations.

Before the changes we made, the SetCell method was invoked on the board parameter. PurityAnalyzer does not allow us to invoke pure-except-locally methods on parameter-based objects.

The state of the PurityAnalyzer extension

The PurityAnalyzer extension is still under development. I have released version 0.6 of the extension so that the reader can experiment with it.

Currently there are 1539 automated tests written for the extension that cover a lot of the requirements that such an extension needs to support.

Still, there are cases where PurityAnalyzer will not detect impure code, and other cases where it will falsely consider pure code to be impure.

PurityAnalyzer works by analyzing code. But sometimes we call compiled methods, i.e. methods that we don’t have the code for, e.g. the .NET framework base class library (BCL).

If we call a compiled method that has any of the attributes that PurityAnalyzer works with, e.g. the IsPure attribute, the extension will correctly handle calls to such methods.

On the other hand, if a method does not have such attributes, there are two ways for PurityAnalyzer to work with it:

1. There is a list of BCL methods/types hardcoded in PurityAnalyzer that it uses to determine the purity of methods. For example, this file (embedded into the extension) contains a list of methods that PurityAnalyzer considers to be pure: https://github.com/ymassad/PurityAnalyzer/blob/master/PurityAnalyzer/Resources/PureMethods.txt

2. Developers can configure PurityAnalyzer to consider some methods/types to be pure. The following figure shows the options page for PurityAnalyzer, which can be accessed using the Visual Studio menu (Tools -> Options, and then selecting Purity Analyzer on the left pane).

purity-analyzer-options

Figure 10: The PurityAnalyzer options page

Developers can create files that contain:

1. methods to consider pure

2. methods to consider pure-except-read-locally

3. methods to consider pure-except-locally

4. methods that return new objects. Objects returned by such methods are considered new objects and therefore it is legal to invoke pure-except-read-locally and pure-except-locally methods on such objects. Please note that for methods that we have the code for, PurityAnalyzer can automatically detect if the called method returns a new object. By the way, you can annotate such methods with the ReturnsNewObjectAttribute.

5. Which types are pure. All methods in pure types are considered pure.

The format of the files is the same as the format of the files in the Resources folder: https://github.com/ymassad/PurityAnalyzer/tree/master/PurityAnalyzer/Resources

The hardest part in making the PurityAnalyzer project succeed is to mark as many BCL methods/types as possible with the appropriate attributes.

This is one thing, you the reader can help with!

The PurityAnalyzer project is open source, you can update the files in the Resources folder and send me a pull request. This is not an easy task though. I spent some 30 minutes trying to figure out which overload of int.TryParse is pure to finally figure out that none of them are pure. I hope that one day the BCL will contain more APIs that are pure.

Conclusion:

In this article I presented PurityAnalyzer; a Visual Studio extension that can help you write pure code. I used the TicTacToe game as an example to show you how to start with code that is not pure and then make the code pure.

By using certain attributes to mark our methods/classes as pure, we can have PurityAnalyzer analyze the code and tell us whether the methods/classes we marked are actually pure. PurityAnalyzer generates errors for code that makes the marked methods impure.

PurityAnalyzer is still under development. I have released version 0.6 so that the reader can experiment with it.

This article was technically reviewed by Damir Arh.

This article has been editorially reviewed by Suprotim Agarwal.

Absolutely Awesome Book on C# and .NET

C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.

We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).

Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.

Click here to Explore the Table of Contents or Download Sample Chapters!

What Others Are Reading!
Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+

Author
Yacoub Massad is a software developer and works mainly on Microsoft technologies. Currently, he works at Zeva International where he uses C#, .NET, and other technologies to create eDiscovery solutions. He is interested in learning and writing about software design principles that aim at creating maintainable software. You can view his blog posts at criticalsoftwareblog.com. He is also the creator of DIVEX(https://divex.dev), a dependency injection tool that allows you to compose objects and functions in C# in a way that makes your code more maintainable. Recently he started a YouTube channel about Roslyn, the .NET compiler.



Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!