Robin Fischer

Game programmer that wants to share and document.

Use 'Format' versions of methods to reduce string.Format() calls with help from Resharper

TL;DR: For in-depth info skip to next paragraph. To reduce unnecessary string.Format() calls use Resharper custom pattern to get hints if there is a ‘Format’ version available. The custom pattern allows automatic optimization of the code.

Introduction

Every time you have to deal with strings while programming you have to be careful about performance and efficient implementation. Strings are so tempting to use for IDs, but you shouldn’t do that. Combining strings is also often used. There are long discussions if you should use a StringBuilder or use the string.Concat method. This post rather discusses the case of unnecessary string combining.

Most often I see unnecessary string combining with logs. Every application writes logs and that is ok. Every logger has different log levels (e.g. info, warning, error, assertion). Look at this snippet:

Logger.Warn(string.Format("Object with name: '{0}' was disabled", obj.name));

This sends out a warning to the log console or log file. Often the Logger is configured to only show the ‘Error’ log level and above. What then happens is that the string.Format() gets executed and the resulting string gets sent to the logger. The logger then checks if the log level is above warning and does not use the string. You have wasted a string.Format() execution. For this reason a lot of frameworks (and hopefully your own one too) introduce the ‘Format’ versions of methods.

Logger.WarnFormat("Object with name: '{0}' was disabled", obj.name);

In this snippet the logger does not execute the string.Format() method when the log is not used.

public static class Logger
{
    private static LogLevel logLevel = LogLevel.Error; 

    public static void Warn(string log)
    {
        if (logLevel <= LogLevel.Warning)
        {
            Log(log);
        }
    }
    
    [JetBrains.Annotations.StringFormatMethod("log")]
    public static void WarnFormat(string log, params object[] args)
    {
        if (logLevel <= LogLevel.Warning)
        {
            Log(string.Format(log, args);
        }
    }
    
    public enum LogLevel
    {
        Info,
        Warning,
        Error,
        Assertion,
    }
}

Automatic conversion to ‘Format’ version

This optimization is often forgotten or new people to the team don’t know about it. To ease this problem I use Resharper ‘Custom Patterns’. These custom patterns are very powerful. Not only can they highlight code that could be optimized, but the patterns can also change your code to the optimized version. The custom pattern for this use case:

alt text

I am using Resharper 9. On future versions this could look different. As you can see it is pretty easy. Unfortunately every log level will need its own ‘Custom Pattern’ because at the moment regex search is not available in custom patterns.

You should save this code patterns to the team-shared file or share them with your teammates somehow. Now the wrong code gets highlighted and with Alt+Enter (default shortcut) you can fix it to the Format version: alt text While we are at it. If you have the source code for your logger you can add the [JetBrains.Annotations.StringFormatMethod("log")] to the ‘Format’ version to improve code highlighting and Resharper help (e.g. insert an argument with Alt+Enter).