C++/C# bad practices: learn how to make a good code by bad example

Amusing C#

To assess the quality C# diagnostics, we test it on a large number of software projects. Since projects are written by different programmers from different teams and companies, we have to deal with different coding styles, shorthand notations, and simply different language features. In this article, I will give an overview of some of the features offered by the wonderful C# language, as well as the issues that one may run into when writing in this language.

Properties and how they can be used

As we all know, a property is a pair of functions – accessor and mutator – designed for writing or reading the value of a field. At least, things used to be that way before the release of C# version 3.0. In its traditional form, a property used to look like this:

Years went by, and both the language standards and properties have acquired a number of new mechanisms.

So, here we go. The C# 3.0 standard brought us the well-known feature that allowed you to omit the field; that is, to declare a property in the following way:

class A
{
public int Index { get; set; }
}

The idea was pushed even further in C# 6.0 by allowing programmers to omit “set” as well:

class A
{
public int Index { get; }
}

It was possible to use this style before C# 6.0 too, but you could not assign anything to a variable declared in such a way. Now it has in fact become an equivalent to readonly fields, i.e. the values of such properties can be assigned only in the constructor.

Properties and fields can be initialized in different ways. For example, like this:

class A
{
public List Numbers { get; } = new List();
}

Or like this:

class A
{
public List Numbers = new List();
}

One more version:

class A
{
public List Numbers => new List();
}

In the last case, though, you will be unpleasantly surprised. You see, what we have actually created there is the following property:

class A
{
public List Numbers { get { return new List(); } }
}

That is, an attempt to fill Numbers with values will inevitably fail; you’ll be getting a new list every time.

A a = new A();
a.Numbers.Add(10);
a.Numbers.Add(20);
a.Numbers.Add(30);

So be careful when using shorthand notations, as it may result in long bug-hunting sometimes.

These are not all the interesting features of properties. As I have already said, a property is a pair of functions, and in C# nothing prevents you from changing the parameters of functions.

For example, the following code compiles successfully and even executes:

However, the program will always output the number “20”, but never “10”.

You may wonder why one would need to assign the value 20 to value? Well, it appears to make sense. To explain this point, however, we’ll have to set our discussion of properties aside for a while and talk about the @ prefix. This prefix allows you to declare variables that resemble keywords in spelling. At the same time, you are not prohibited from inserting this character wherever you please, for example:

A few words about LINQ queries

LINQ queries are in themselves a convenient feature: you make a sequence of necessary samples and get the required information at the output. Let’s first discuss a couple of nice tricks that may not occur to you until you see them. Let’s start with a basic example:

Now let’s talk about the specifics of LINQ-query execution. For example, the following code line won’t trigger immediate sampling of data from the numbers1 collection.

IEnumerable selection = numbers1.Where(whereFunc);

Sampling will start only after the sequence has been converted into the List collection:

List listNumbers = selection.ToList();

This nuance may cause a captured variable to be used after its value has changed. Here’s a simple example. Suppose we need function Foo to return only those elements of the “{ 1, 2, 3, 4, 5 }” array whose numerical values are less than the current element’s index. In other words, we need it to output the following:

The problem with our code has to do with the closure in the lambda expression index => index < i. The i variable was captured, but because the lambda expression index => index < i was not called until the string.Join(“, “, subArray.Value) function was requested to return, the value that the variable referred to was not the same as when the LINQ query had been formed. When retrieving data from the sample, the i variable was referring to 5, which resulted in incorrect output.

Undocumented kludges in C#

The C++ language is famous for its hacks, workarounds, and other kludges – the series of XXX_cast functions alone counts for a lot. It is commonly believed that C# doesn’t have any such things. Well, it’s not quite true…

Here are a few keywords, for a start:

__makeref

__reftype

__refvalue

These words are unknown to IntelliSense, nor will you find any official MSDN entries on them.

So what are these wonder words?

__makeref takes an object and returns some “reference” to it as an object of type TypedReference. And as for the words __reftype and __refvalue, they are used, respectively, to find out the type and the value of the object referred to by this “reference”.

The dynamic keyword allows us to both use fewer lines and avoid questions like “What’s that?” and “How does it work?” that programmers not familiar with those words may ask. That’s fine, but here’s a somewhat different scenario where dynamic doesn’t look that great compared to TypedReference.

It is strange that the foreach statementcan’t be used as an out-of-the-box solution to iterate through a list or access a list element directly. So, it’s not that cool as C++ or JavaScript with its arguments 🙂

Conclusion

To sum it up, I’d like to say that C++ and C# are highly flexible languages as far as their grammar goes, and that’s why they are convenient to use on the one hand, but don’t protect you from typos on the other. There is an established belief that in C# it’s impossible to make such mistakes as in C++, but it’s just not true. This article demonstrates rather interesting language features, but the bulk of errors in C# has nothing to do with them; instead, they typically occur when writing common if-inductions, like in Infragistics project. For example:

V3001 There are identical sub-expressions ‘double.IsNaN(Latitude)’ to the left and to the right of the ‘||’ operator. WeatherStation.cs 25

It is at points like this that human attention tends to weaken, which causes you later to waste a huge amount of time trying to track down “God-knows-what–God-knows-where”. So don’t miss the chance to protect yourself from bugs with the help of PVS-Studio static code analyzer.