5 CSharp Tips That you may already know

5 CSharp tips that will help you write more clean/efficient code

Wed, 27 Feb 2019

Banner

5 CSharp tips that you may not know

I acknowledge that you may already know most of these tips already. But I write in the hope that if someone in the community can benefit from learnings. If you want to find out if there is anything useful for you in this post have a quick look at the headings.

Note to all readers

I want to make clear that i do not think of me as an expert of the topic. I understand few of you find out these tips are not correct or not inline with your thinking/opinion. These tips will serve better to individuals who will look at them as starting point and continue their own exploration/learning and may end up finding that these tips were not the best way to do things and find much better ways. If you are still interested and think you have 2 mins to spare please go ahead and you may find something useful.

1.Avoid reading the Http response to a temporary string

Recently i read a tweet on twitter from David Fowler criticizing reading whole Http response to string a pattern that he observed being used by many developers in the community. Then i went out on a pursuit to find out why it is so bad. I understand there is temporary allocation involved and more un-necessary allocations more work for garbage collector that can adversely effect your applications. But that’s not the whole story there is a possibility of even bigger issue in such allocation.

Http request Responses can be sometime really big in size and can even go above the limit of the 85kb. If they go above the limit of 85kb in size they will be allocated on LOH(Large object heap) that is bad enough news to avoid these sort of un-necessary allocations. Want to read more about the danger of Large object heap why you should try to avoid where possible. Here is an article about how Large Object Heap Works

Below is an example how you can avoid reading response of an Http Request into a temporary string for De-serialization this example uses JSON.NET for Serialization processing.

Credit for this code sample goes to JSON.NET documentation website with few changes

var client = new HttpClient();
using (var s = await client.GetStreamAsync("http://www.nerdlife.me"))
using (var sr = new StreamReader(s))
using (var reader = new JsonTextReader(sr))
{
    var serializer = new JsonSerializer();
    // Read the json response for the request from the stream
    // Json size doesn't matter because only a small piece is read at a time from the HTTP request
    var p = serializer.Deserialize<Person>(reader);
}

2.Replace temporary/un-necessary collections with Yield

This is fairly common scenario that we come across while writing applications. In this scenario we have a method that will return a collection.

 [TestMethod]
        public void TestWithoutYield()
        {
            // Display powers of 2 up to the exponent of 8:
            foreach (int i in PowerWithoutYield(2, 8))
            {
                Debug.Write("{0} ", i);
            }
           
        }

public static IEnumerable<int> PowerWithoutYield(int number, int exponent)
    {
        int result = 1;
        var results = new List<int>();
        for (int i = 0; i < exponent; i++)
        {
            result = result * number;
            results.Add(result);
        }
        return results;
    }

In this example we created a method that Raise the first parameter to the power of second parameter. What we did we created a temporary collection to new data and added new values to this temporary collection and returned the collection.

We can use yield to eliminate the need of these upfront temporary allocations that was used for the resulting list (var results = new List<int>()).

If we refactor the above code snippet to use Yield.The above code sample will change to something like below.

“Code example is inspired from MSDN website”

[TestMethod]
        public void TestWithYield()
        {
            // Display powers of 2 up to the exponent of 8:
            foreach (int i in PowerWithYield(2, 8))
            {
                Debug.Write("{0} ", i);
            }
           
        }

public static IEnumerable<int> PowerWithYield(int number, int exponent)
    {
        int result = 1;
        for (int i = 0; i < exponent; i++)
        {
            result = result * number;
            yield return result;
        }
    }

Have you noticed how we do not need a temporary upfront list to hold new numbers from the method PowerWithYield.

By temporary allocation means some allocation that we just used to hold return results and was not serving any other purpose.

Avoiding upfront temporary allocations results in efficient utilization of available memory of your application. But avoiding upfront temporary allocations is not the only benefit of Yield there is more to it sometimes it can even result in avoiding few un-necessary computations or processing as it results in deferred execution. But explaining everything about yield and how it works deserves a post on its own.

From the feedback of very few readers it looks like people find it hard to understand how it can avoid allocation. I created two gists to demonstrate the difference between them. We will analyze the Decompiled code to find out how actually Yield can help avoid the allocation.

  1. Here is the link for the Non Yield Example

As you can see there is a collection to hold the results of the function execution and this temporary collection can be avoided using Yield keyword.

In order to highlight the difference i created another gist that show the Decompiled code for the Yield example. And you can see in the Gist that Yield method is converted into a StateMachine that implements IEnumerable interface and Then calling method get the enumerator of StateMachine using GetEnumerator and Use move next to iterate through the values that will be returned by the Yield method. So we avoided an allocation for the Intermediate/Temporary list to hold return results.

  1. Here is the link for the Yield Example

There are situations where using Yield will result in overhead that will overweight the benefits of avoiding some upfront temporary allocations. Specially when the collection you are going to return from a method is small in size. In that case cost of creating StateMachine will overweight the upfront cost of Temporary allocations. But you can benefit if you are going to return a huge collection in that case you can avoid the allocation cost of List to hold results.

I just highlighted one of the benefits that ‘yield’ can offer in some scenarios. But highly encourage you to explore more. I will leave you with something that may encourage to explore more about the yield and how it works in C#.

By looking at the code what you think whether the Unit Test will pass or fail due to unhandeled exception ?

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace CSharpTipsProject
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void GeEvenNumbersWhereYieldReturnThrowException()
        {
            var numbersToFilter = Enumerable.Range(1, 20);
            var evenNumbers = GetEvenNumbersWithYield(numbersToFilter);
            Assert.IsNotNull(evenNumbers);
        }

        private IEnumerable<int> GetEvenNumbersWithYield(IEnumerable<int> numbers)
        {
            Debug.WriteLine("Filtering Even Numbers");
            throw new Exception("Exception in get even numbers.");
            foreach (var num in numbers)
            {
                Debug.WriteLine("Processing input number: "+num);
                if (num % 2 == 0)
                {
                    yield return num;
                }
            }
        }
    }
}

3.Marking Code as Obsolete

If you ever came across a situation where you have some functionality available in a library or application. That you are not ready to remove completely yet. But you want to discourage the use of that functionality.

Then CSharp already provides a attribute that allows to decorate methods and compiler will warn callers about depreciation and discourage them to use it.

If you want to just show the warning to the consumer with a user friendly message you can use something like below. When user will compile the code compiler will generate a build warning.

For Warning:

[Obsolete("This method has been superseded by the IsInUse() method")]
public void NotInUse()

If you want to completely disallow the use of a given functionality. You can pass the second parameter as ‘true’ to the Obsolete attribute and it will break the compilation of the code. Piece of code using the obsolete method will not be compiled without errors.

For Error:

[Obsolete("This method has been superseded by the IsInUse() method", true)]
public void NotInUse()

4. Preserving stack trace when re-throwing exceptions

You must have written a piece of code in your application. Where you have try and catch block and inside the catch block instead of handling the exception. You read the exception data Debug Log somewhere and then rethrow the exception so it can bubble up call stack and then you handle that exception at some central place in your application.

In order to demonstrate this feature I created a dummy exception factory that will throw exception and code for the factory and it looks like

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace RethrowExample
{
    public class ExceptionThrowingFactory
    {

        //Method that will loose the stack trace
        public void NoStackTrace()
        {
            try
            {
                FirstLevelDeep();
            }
            catch (Exception ex) //Typical try catch pattern
            {
                Debug.WriteLine($"Exception with message: {ex.Message}");
                throw ex;
            }
        }
        //Method that will preserve the stack trace
        public void WithStackTrace()
        {
            try
            {
                FirstLevelDeep();
            }
            catch (Exception ex) //Typical try catch patter
            {
                Debug.WriteLine($"Exception with message: {ex.Message}");
                throw;
            }
        }
        //Dummy method to make call stack 3 levels deep
        public void FirstLevelDeep()
        {
            SecondLevelDeep();
        }

        public void SecondLevelDeep()
        {
            ThreeLevelDeep();
        }

        public void ThreeLevelDeep()
        {
            throw new Exception("Just a demo exception.");
        }

    }
}

Now we are going to call the both methods NoStackTrace and WithStackTrace from the factory class by wrapping them into a unit test.

 [TestClass]
    public class TestStackTrace
    {
        [TestMethod]
        public void TestStackTraceLostForExceptions()
        {
            var factory = new ExceptionThrowingFactory();
            factory.NoStackTrace();
        }

        [TestMethod]
        public void TestStackTracePreservedForExceptions()
        {
            var factory = new ExceptionThrowingFactory();
            factory.WithStackTrace();
        }
    }

Let’s debug these both tests one by one. First we execute the Test Case TestStackTraceLostForExceptions and we will look at the resulting StackTrace.

//Original stack for the failed method
Result StackTrace:	
at RethrowExample.ExceptionThrowingFactory.NoStackTrace() in C:\Users\ranje\Documents\Visual Studio 2017\Projects\RethrowExample\RethrowExample\ExceptionThrowingFactory.cs:line 20
   at RethrowExample.TestStackTrace.TestStackTraceLostForExceptions() in C:\Users\ranje\Documents\Visual Studio 2017\Projects\RethrowExample\RethrowExample\TestStackTrace.cs:line 12

As we can see the StackTrace suggest that the exception happened in the method NoStackTrace() and we lost the original call stack information from where exception was originated.

Now let’s debug the second Test Case TestStackTracePreservedForExceptions and have a look at the StackTrace for exception captured.

Result StackTrace:	
at RethrowExample.ExceptionThrowingFactory.ThreeLevelDeep() in C:\Users\ranje\Documents\Visual Studio 2017\Projects\RethrowExample\RethrowExample\ExceptionThrowingFactory.cs:line 48
   at RethrowExample.ExceptionThrowingFactory.SecondLevelDeep() in C:\Users\ranje\Documents\Visual Studio 2017\Projects\RethrowExample\RethrowExample\ExceptionThrowingFactory.cs:line 43
   at RethrowExample.ExceptionThrowingFactory.FirstLevelDeep() in C:\Users\ranje\Documents\Visual Studio 2017\Projects\RethrowExample\RethrowExample\ExceptionThrowingFactory.cs:line 38
   at RethrowExample.ExceptionThrowingFactory.WithStackTrace() in C:\Users\ranje\Documents\Visual Studio 2017\Projects\RethrowExample\RethrowExample\ExceptionThrowingFactory.cs:line 27
   at RethrowExample.TestStackTrace.TestStackTracePreservedForExceptions() in C:\Users\ranje\Documents\Visual Studio 2017\Projects\RethrowExample\RethrowExample\TestStackTrace.cs:line 19

Now we can see the stack trace contains the information about the complete call stack where the exception was originated from and information about the complete call stack for execution.

Summary of above discussion is:

//Using something like 
// we will loose the stack trace information from the original exception 
...
catch (Exception ex) 
            {
                Debug.WriteLine($"Exception with message: {ex.Message}");
                throw ex;
            }

//Using just throw keyword preserve the original stack trace
...
catch (Exception ex) 
            {
                Debug.WriteLine($"Exception with message: {ex.Message}");
                throw;
            }

5.Use your custom serializer and de-serializer for classes using JSON.NET

If you already have JSON.NET in your tech stack and don’t want any other serialization dependency but want to squeeze more performance out of the serialization and de-serialization using JSON.NET then you can think about writing custom serializer and de-serializer for your classes. Writing custom serializer will allow you to avoid the cost of Reflection used by JSON.NET but result will still may be slower than other serialization libraries that just focus on performance but that performance optimization come at the cost of loosing other benefits.

Writing custom serializer for your classes you need to write more code. But if there are one or two classes in your application that are used for Serialization and De-Serialization then you can write custom serializer for those classes only. If there are too many classes that need custom serializer and your application really focus on performance then look for alternative libraries that have better performance results as compared to JSON.NET.

The absolute fastest way to read and write JSON is to use JsonTextReader/JsonTextWriter directly to manually serialize types. Using a reader or writer directly skips any of the overhead from a serializer, such as reflection - (From JSON.NET Documentation Website)

Below is an example of writing custom serializer using JSON.NET. Where you can use JsonTextReader to read the String data of a object and parse it into C# object of type Car.

Note: The code below is for demonstration purposes not production ready.

public class Car {
    public string Make {get;set;}
    public string Model {get;set;}
    public string Year {get;set;}
    // other fields so on ...
} 
//Example to De-Serialize Car Object string into an object
private static Car DeserializeCar(string carInfo)
        {
            using (var reader = new JsonTextReader(new StringReader(carInfo)))
            {
                string model;
                var make = model = string.Empty;
                int year = 0;
                var currentProperty = string.Empty;

                while (reader.Read())
                {
                    if (reader.Value != null)
                    {
                        if (reader.TokenType == JsonToken.PropertyName)
                            currentProperty = reader.Value.ToString();

                        if (reader.TokenType == JsonToken.Integer && currentProperty == "Year")
                            year = Int32.Parse(reader.Value.ToString());

                        if (reader.TokenType == JsonToken.String && currentProperty == "Make")
                            make = reader.Value.ToString();

                        if (reader.TokenType == JsonToken.String && currentProperty == "Model")
                            model = reader.Value.ToString();
                        // Process other fields
                    }

                }
                return  new Car(make, model, year);
            }
        }

//Example to Manually serialize the Car Object using extension method
public static string ConvertToJson(this Car c)
{
    var sw = new StringWriter();
    var writer = new JsonTextWriter(sw);
    //Start writing serialized object
    writer.WriteStartObject();
    //Write the first property name
    writer.WritePropertyName("make");
    //Write the property value
    writer.WriteValue(p.Make);
    writer.WritePropertyName("model");
    writer.WriteValue(p.Model);
    writer.WritePropertyName("yodel");
    writer.WriteValue(p.Year);
    //Finish writing object add ending curly brace.
    writer.WriteEndObject();
    //Return the resulting serialized string
    return sw.ToString();
}

Time to say good bye

I understand you may already know most of these tips already. But I write in the hope that if someone in the community can benefit from learnings. If you think this post helped you a bit please let me know in comments any feedback is much appreciated. If you like my posts and don’t want to miss my any future post please follow me on twitter at @NotRanjeet. I will see you guys soon with the a new post.

Loading...
Ranjeet Singh

Ranjeet Singh Full Stack Developer (.NET, ReactJS, Redux, Azure) You can find me on twitter @NotRanjeet or on LinkedIn at LinkedIn