76
\$\begingroup\$

What general tips do you have for golfing in C#? I'm looking for ideas that can be applied to code golf problems in general that are at least somewhat specific to C# (e.g. "remove comments" is not an answer). Please post one tip per answer.

-- borrowed from marcog's idea ;)

\$\endgroup\$
1
  • \$\begingroup\$ Forgive me for the picture of a calendar, it was all I could find on short notice. \$\endgroup\$ Jul 8, 2014 at 12:44

59 Answers 59

1
2
4
\$\begingroup\$

Use Action like Func to set a function to a variable. Action returns nothing (void) so it is great for printing.

For example:

Action<string>w=Console.WriteLine;
w("Hello World");

This tips is inspired by @W0lf great example of use of Func with ReadLine.

\$\endgroup\$
4
\$\begingroup\$

Declare empty/matching strings together

If you need to declare multiple empty/matching strings, you can save a few bytes with the following:

string a="";string b="";string c=""; // 36 bytes
var a="";var b="";var c="";          // 27 bytes
string a="",b="",c="";               // 22 bytes
string a="",b=a,c=a;                 // 20 bytes

Unfortunately var a="",b=a,c=a; is illegal, as implicitly type variable cannot have multiple declarators

\$\endgroup\$
2
  • \$\begingroup\$ Can you do var a=b=c="" like in javascript? \$\endgroup\$
    – corvus_192
    Jan 11, 2017 at 10:22
  • 1
    \$\begingroup\$ @corvus_192 nope - unfortunately not. \$\endgroup\$
    – Erresen
    Jan 11, 2017 at 10:41
4
\$\begingroup\$

Use the one character non-short-circuiting variants of logical operators where possible:

  • i>0||i<42
  • i>0|i<42

or

  • i>0&&i<42
  • i>0&i<42

The difference between the two are one byte (yeah!) and the short-circuit principle. In our first example if i>0 is true, i<42 wont be checked. We dont need it. With the bitwise, both will be evaluated.

example: Code golf to make logos for New Stack exchange sites

Learn more about them on MSDN.

\$\endgroup\$
6
  • 1
    \$\begingroup\$ Your statement about short circuiting is incorrect, logical or (||) does indeed short circuit if the left operand is true. msdn.microsoft.com/en-us/library/6373h346.aspx \$\endgroup\$
    – user19547
    Jul 11, 2016 at 21:56
  • \$\begingroup\$ @Phaeze Indeed I wrote the wrong one. Thank you. \$\endgroup\$
    – aloisdg
    Jul 11, 2016 at 23:55
  • \$\begingroup\$ Technically none of these are bitwise operators, they are just non-short-circuiting Boolean operators, because they are operating on Booleans. \$\endgroup\$ Jan 13, 2017 at 21:17
  • \$\begingroup\$ @VisualMelon MSDN uses "logical or bitwise OR" for "x | y" and "logical OR" for "x || y". What should I use? \$\endgroup\$
    – aloisdg
    Jan 14, 2017 at 15:21
  • 1
    \$\begingroup\$ These are 'logical' (Boolean being the logic system). Bitwise would be for integer types. I like to make the distinction clear because the a lot of C# golfing can end up being a mix of both, and operator precedence can be a real pain (though they are the same for each type) \$\endgroup\$ Jan 14, 2017 at 17:16
4
\$\begingroup\$

(A particular case of knowing your operator precedence!)

Use % for tight-binding (somewhat) limited subtraction. This can save you a pair of parentheses around a subtraction, the result of which you want to multiply or divide by something; but be careful, it has serious limitations.

Instead of

char b='5'; // b is some ASCII input
int a=(b-48)*c; // we want to find the numerical value of b, and multiply it by something ('0'==48)

Consider

char b='5'; // b is some ASCII input
int a=b%48*c; // only good for ASCII within 48 of '0' (positive only)!

Examples:

'5'%'0'*2 -> 10
'5'%'0'*-1 -> -5
'5'%'0'/2 -> 2

I've only just discovered this, and I feel like it will be valuable thing to remember whenever working with ASCII in the future. (I'm currently golfing somewhere where I'm using ASCII for compact numeric representations, but needs to multiply by 1 or -1 based on another condition, and this striped 2 bytes)

\$\endgroup\$
4
\$\begingroup\$

If you're already using Linq in your answer and need to check for a none empty collection use Any(). Compare it to the following:

Count()>0
Length>0
Count>0
Any()
\$\endgroup\$
4
\$\begingroup\$

Use Ranges and indices (C# 8)

You can use the type Index, which can be used for indexing. You can create one from an int that counts from the beginning, or with a prefix ^ operator that counts from the end:

Index i1 = 3;  // number 3 from beginning
Index i2 = ^4; // number 4 from end
int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Console.WriteLine($"{a[i1]}, {a[i2]}"); // "3, 6"

You can also use a Range type, which consists of two Indexes, one for the start and one for the end, and can be written with a x..y range expression. You can then index with a Range in order to produce a slice:

var slice = a[i1..i2]; // { 3, 4, 5 }

source

\$\endgroup\$
4
\$\begingroup\$

C# Interactive Window

AKA C# (Visual C# Interactive Compiler) on Try it Online

C# Interactive

This is a REPL for the C# language that includes many advantages to code golfing over using the traditional C# compiler.

Currently, the shortest full program to print Hello, World! using the traditional C# compiler is 67 bytes:

class P{static void Main(){System.Console.Write("Hello, World!");}}

Using the C# Interactive Window, you can do the same thing in 22 bytes:

Write("Hello, World!")

And yes, I added my answer just for this post ;)

As you can see, ceremonial class definitions are not required. Also, a whole bunch of references are included by default. The current list is as follows:

  • /r:System
  • /r:System.Core
  • /r:Microsoft.CSharp
  • /u:System
  • /u:System.IO
  • /u:System.Collections.Generic
  • /u:System.Console
  • /u:System.Diagnostics
  • /u:System.Dynamic
  • /u:System.Linq
  • /u:System.Linq.Expressions
  • /u:System.Text
  • /u:System.Threading.Tasks

The System.Linq namespace is particularly handy as it allows you to write functional style programming which often saves many bytes. Many of the older C# answers added 18 bytes or so for this advantage, simply to import the library using System.Linq;. There are other hacks where solutions created their class in the System namespace to more succinctly access the Console object. None of this is needed with the C# Interactive Window.

\$\endgroup\$
4
\$\begingroup\$

Group statements via tuples

Let's assume the simplest case: you have two statements that return a value and would like to group them into one that returns the second value (perhaps the first one has useful side effects):

(s1,o:s2).o

This can be used along with inline variable declarations via is:

(s1 is var x,o:f(x,g(x))).o

It also works with the void -> value transformation via is int (although it doesn't always help):

(Print(x)is int,o:f(x)).o
\$\endgroup\$
1
  • 2
    \$\begingroup\$ (s1 is var x,o:f(x,g(x))).o can usually be shortened to (s1 is{}x,o:f(x,g(x))).o now. \$\endgroup\$
    – recursive
    Dec 20, 2022 at 18:40
3
\$\begingroup\$

Discovered tonight "in the trenches" while improving some golf code... if you have a class for your processing, you can do the work in the constructor to save declaring a method.

I discovered this while reducing a console application - as there was a static void Main(), all functions and variables had to be declared static. I created a nested class with member functions and variables, with the main work performed in the constructor. This also saves characters in the calling code.

e.g. Class with method:

class a
{
    public void b()
    {
        new c().d("input");
    }
}
class c
{
    public void d(string e)
    {
        System.Console.Write(e.Replace("in", "out"));
    }
}

Class with work in the constructor:

class a
{
    public void b()
    {
        new c("input");
    }
}
class c
{
    public c(string e)
    {
        System.Console.Write(e.Replace("in", "out"));
    }
}

This example saves 9 characters.

\$\endgroup\$
3
\$\begingroup\$

When to use a space and when you can remove it.

After []

  • int[] f(char[] a){Console.Write('a');}
  • int[]f(char[]a){Console.Write('a');}

Before $

  • return $"{a}"
  • return$"{a}"

example: Code golf to make logos for New Stack exchange sites


(Add yours in comment I will edit)

\$\endgroup\$
3
\$\begingroup\$

When you want to join something to output a string without delimiter, you should use string.Concat(), instead of string.Join("",);

  • string.Join("",)
  • string.Concat()

One byte free!

example: Code golf to make logos for New Stack exchange sites

Also Concat() has a lot more signatures than Join. Check them on MSDN: Concat and Join.

\$\endgroup\$
3
\$\begingroup\$

If you need to use an enum for a method it is often shorter to cast an int to it rather than using the value directly:

DayOfWeek.Sunday
(DayOfWeek)0;
\$\endgroup\$
3
\$\begingroup\$

Always use the alias for a type if you need to type as they are usually shorter than the full names. It also means you don't need to include

using System;

or fully qualify the type when you otherwise wouldn't need to.

For a full list of the aliases visit this SO answer:

object:  System.Object
string:  System.String
bool:    System.Boolean
byte:    System.Byte
sbyte:   System.SByte
short:   System.Int16
ushort:  System.UInt16
int:     System.Int32
uint:    System.UInt32
long:    System.Int64
ulong:   System.UInt64
float:   System.Single
double:  System.Double
decimal: System.Decimal
char:    System.Char

Note that you can also use the alias for calling static methods, consider:

System.String.Concat();

vs

string.Concat();
\$\endgroup\$
3
\$\begingroup\$

If you want to return multiple values from a function, use C# 7 style tuples instead of out parameters:

(int,int)d(int a,int b)=>(a/b,a%b);
\$\endgroup\$
2
  • \$\begingroup\$ Oh, didn't knew this was possible in C#. Thanks for sharing, +1 from me! One thing to golf in your code is by using currying input, though (a=>b=> instead of (a,b)=>). Here is your code in action, which might be useful to add as example-link to your tip. Oh, and welcome to PPCG! :) \$\endgroup\$ Aug 31, 2018 at 14:53
  • \$\begingroup\$ @KevinCruijssen posted here :) \$\endgroup\$
    – aloisdg
    Jan 11, 2019 at 9:14
3
\$\begingroup\$

Here are my 5 cents:

You can plain replace Math.Max and Math.Min, the following way, to gain 6 bytes:

Math.Max(a,b); // turns into:
a>b?a:b;       // saving 6 bytes

Another hint is a small way to use a loop, when you are given a variable with "n" times that won't be used anymore (as it will flatten your variable);

for (int i=0;i<n;i++) // turns into: 
while (n-->0)         // saving 8 bytes

Method string.Split(' ') has default value whitespace, so if input string has the usual ' ' separation, instead of:

string.Split(' ');

Write:

string.Split() // gain 3 bytes
\$\endgroup\$
3
  • \$\begingroup\$ Some of these become easier with newer .net versions. I should've probably known ahead of time to lock it in at a specific version. These are good optimizations. \$\endgroup\$
    – jcolebrand
    Aug 24, 2020 at 19:32
  • 1
    \$\begingroup\$ Not locking it allows the answers to improve and keep alive and updated. \$\endgroup\$
    – Zuabros
    Aug 24, 2020 at 20:17
  • 2
    \$\begingroup\$ For nitpicking: “has default value space” actually should be “has default value whitespace”. So without ' ' the string will be split also on tab, newline and a few more characters. (“If the separator parameter is null or contains no characters, white-space characters are assumed to be the delimiters. White-space characters are defined by the Unicode standard and return true if they are passed to the Char.IsWhiteSpace method.” — String.Split Method) \$\endgroup\$
    – manatwork
    Sep 21, 2020 at 0:59
3
\$\begingroup\$

If you're already using Linq in your code and need to create a list use the following:

var l=new int[0].ToList();

Compared to:

var l=new System.Collections.Generic.List<int>();

You can even initialise the list with values like:

var l=new []{1,2,3,4}.ToList();
\$\endgroup\$
5
  • 1
    \$\begingroup\$ You can remove the 0 in your declaration. \$\endgroup\$
    – PmanAce
    Feb 20, 2019 at 19:35
  • \$\begingroup\$ new int[0]{1,2,3,4} is a syntax error. perhaps new[]{1,2,3,4} instead ? \$\endgroup\$
    – Caius Jard
    Feb 16, 2021 at 9:19
  • \$\begingroup\$ @CaiusJard Probably a typo, removed it. \$\endgroup\$ Feb 16, 2021 at 9:27
  • \$\begingroup\$ ` int` can be removed too for some more golfy goodness if it can infer the type from the init'r \$\endgroup\$
    – Caius Jard
    Feb 16, 2021 at 9:29
  • \$\begingroup\$ @CaiusJard Good point, I think I've even done that a few times as well \$\endgroup\$ Feb 16, 2021 at 9:32
3
\$\begingroup\$

Infinite Loops

Default infinite loop provided in code golf is usually a while loop:

while(true){...}

This can be reduced (-1, as stated earlier) using an inequality:

while(1>0){...}

But it can be better reduced (-4) using a parameterless for command:

for(;;){...}
\$\endgroup\$
3
  • \$\begingroup\$ Hey there, welcome to the code golf stack exchange! Great first answer! \$\endgroup\$
    – lyxal
    Nov 24, 2022 at 10:03
  • \$\begingroup\$ This seems identical to the C answer \$\endgroup\$
    – mousetail
    Nov 24, 2022 at 16:03
  • \$\begingroup\$ There's also l:...goto l; Which is same length as a parameterless for when you can't remove the for's brackets \$\endgroup\$
    – nalka
    Jun 7, 2023 at 1:33
2
\$\begingroup\$

Implicitly typed arrays

If you need an array (or IEnumerable) initialized with constant data, the most naive implementation might be like so:

new int[6]{23,16,278,0,2,44}

It's well known that arrays in C# can have implicit length:

new int[]{23,16,278,0,2,44}

However, for certain data types C# will also be able to determine the base type for the array from its values, making the same declaration even shorter:

new[]{23,16,278,0,2,44}

Here is an example demonstrating that all these options create the same array.

Additionally, specifically if you are creating an array for inline declaration, the new[] can be removed as long as the declared variable is not implicitly typed, which can save some bytes:

var a=new[]{"woah"};
string[]b={"woah"};
\$\endgroup\$
1
  • 1
    \$\begingroup\$ That last line is worth all the weight. Excellent tips! \$\endgroup\$
    – jcolebrand
    Nov 15, 2017 at 18:58
2
\$\begingroup\$

Convert a string to an IEnumerable<char>

Common way would be to use .ToCharArray() (14 bytes) or even better .ToList() (11 bytes), but the best I found is to rely on .Skip(0) (8 bytes).

Most of the time you wont need anything and the string will be cast directly, but sometimes it matters:

string.Join("a","bb") // bb

vs

string.Join("a","bb".Skip(0)) // bab
\$\endgroup\$
4
  • 1
    \$\begingroup\$ wait ToSkip is a thing? \$\endgroup\$
    – ASCII-only
    Apr 27, 2018 at 7:50
  • \$\begingroup\$ @ASCII-only Skip ;) \$\endgroup\$
    – aloisdg
    Apr 27, 2018 at 11:03
  • \$\begingroup\$ Then it's no longer 10 bytes \$\endgroup\$
    – ASCII-only
    Apr 27, 2018 at 11:05
  • \$\begingroup\$ @ASCII-only yup. 8 bytes \o/ \$\endgroup\$
    – aloisdg
    Apr 27, 2018 at 11:07
2
\$\begingroup\$

Tuple Deconstruction

It is possible to deconstruct a Tuple into variables using the following syntax:

var myTuple = (1, "red", new DateTime(2000, 1, 1));
var (i, c, d) = myTuple;

In a code-golf scenario, you could use this to pass a tuple into a function:

t=>{var(i,c,d)=t;Print(i);Print(c);Print(d);}

Try it online!

A more practical use of this is to decompose a list of tuples and iterate over them using foreach.

var myTuples = new[] {
    (1, "red", new DateTime(2000, 1, 1)),
    (2, "blue", new DateTime(2000, 1, 1))
};
foreach (var (i, c, d) in myTuples) {
    Print(i);
    Print(c);
    Print(d);
}

Or in a code-golf scenario:

t=>{foreach(var(i,c,d)in t){Print(i);Print(c);Print(d);}}

Try it online!

\$\endgroup\$
2
\$\begingroup\$

Use Range instead of Substring:

var s="abcdef";
Console.Write(s.Substring(2,2)); // "cd"
Console.Write(s[2..4]); // "cd"

It doesn't work if type is not known at compile time:

dynamic s="abcdef";
Console.Write(s[2..4]); // runtime error
Console.Write($"{s}"[2..4]); // "cd"
\$\endgroup\$
1
\$\begingroup\$

You can use the ternary operator to shorten complex if..else constructs even if you need to work on multiple or different variables in different branches. You can sometimes save some chars by doing all or part of the work in the 3rd part of a for. ...and also other "optimizations", you can find in this example, I submitted here (it increments all numbers in a string given as char array or StringBuilder):

for (int b=3, c, i=a.Length; i-->0;
    b=48>c|c>57
        ?7
        :b>2
            ?c>56?a[i]='0':++a[i]*0
            :b
) c=a[i];

In two of the branches, b isn't really set; in two branches, a[i] is set even though it says b= in the beginning; in one case a[i] and b are set simultaneously...

c>56 is shorter than c==57

i=a.Length; i-->0; is a lot shorter than i=a.Length-1; i>=0; i--

\$\endgroup\$
1
\$\begingroup\$

One thing I just learned (which I didn't know but probably all of you do): You do not need a namespace at all. If it is a simple program which doesn't use many namespaces that you would need to shorten, just omit the namespace and start with class Foo{....

Any objections to this or hints why I shouldn't do this are very welcome, as I'm just writing up my first "golfed" (as far as I got) answer in C# ;-)

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Alternately by putting the namespace as system you can omit the common using System; declaration ... \$\endgroup\$
    – jcolebrand
    Oct 29, 2014 at 16:30
1
\$\begingroup\$

Convert line endings from CRLF to LF before counting bytes ☺

\$\endgroup\$
3
  • 3
    \$\begingroup\$ This really applies to all languages, and preprocessor directives are pretty rare in C# golf, so few programs will ever need to be on more than one line. \$\endgroup\$ Jan 3, 2015 at 15:42
  • \$\begingroup\$ Mh but it's still non-obvious to some people. And most other languages are more often seen on LF-only environments. I don't expect it to help much, but… why not? \$\endgroup\$
    – mirabilos
    Jan 3, 2015 at 15:43
  • 9
    \$\begingroup\$ Most of the time, just remove newlines... \$\endgroup\$
    – aloisdg
    Jul 8, 2016 at 15:04
1
\$\begingroup\$

ReSharper tips

If you have used ReSharper to get to the initial working solution before golfing, note that it often generates

  • readonly variables
  • static methods, if they do not use any fields

If you have R#, you want to use the inline method refactoring for methods that are only called once, since they only need the additional method declaration.

\$\endgroup\$
1
  • \$\begingroup\$ Or Rider, which is currently in free beta (unlike R#). \$\endgroup\$
    – user42643
    Jul 8, 2016 at 15:41
1
\$\begingroup\$

Remember that C# uses unicode (which includes ascii). All char are int under the hood.

For example 'a' is 97.

  • n=>char.IsDigit(n)|char.IsUpper(n)
  • n=>n>47&n<58|n>64&n<91 // note that I use a bitwise comparator see

example: Code golf to make logos for New Stack exchange sites


Since char are int, you can increment them:

  • for(int x=31;x<126;)Console.Write((char)++x);
  • for(char x=' ';x<127;)Console.Write(x++);

example: Print the ASCII printable character set

Use the unicode table for reference. Be careful one byte != one character.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ ints are 4 bytes, but chars are only 2. \$\endgroup\$
    – recursive
    May 21, 2019 at 20:06
1
\$\begingroup\$

Using Command-Line Options

Command-line options are not included in the byte count of your program, so they are very useful. However, keep in mind that when you use a command-line option, you are not competing in C# anymore, you are competing in C# with -yourflag.

The syntax for them is to put a - or a / preceding the letter, and if there are any arguments, you use : to separate the option name from the argument.

There are 3 command-line options that I know of as of right now:

  • /u or -u or /using or -using

    • Acts as a using statement. Example: /u:System.Text.RegularExpressions. You can do static imports with them by just adding the class name, like /u:System.Text.RegularExpressions.Regex. For example, if you have /u:System.String as an option, you can just use Concat(yourString) instead of string.Concat(yourString). Sadly, aliasing doesn't seem to work. You can also put multiple imports on the same line with ; as a delimiter like this: /u:System.Math;System.CodeDom.
  • /i or -i, activates REPL mode. Code from the input box is also evaluated and printed.

    • Can be useful for situations where hard-coding is allowed, e.g. if input insertRandomTermHere output 3, if input anotherRandomTerm output 4, we can just do int insertRandomTermHere=3,anotherRandomTerm=4;. Try it online!
  • /r or -r

    • Acts as an assembly reference. Allows you to use types like those in System.Numerics or System.Drawing. If you want to use BigInteger, an assembly reference is required.

Meanwhile, happy ing!

\$\endgroup\$
5
  • \$\begingroup\$ Where did you find these flags in the docs? I can only find something about the default Roslyn flags. \$\endgroup\$ Jan 30, 2019 at 10:55
  • 1
    \$\begingroup\$ @KevinCruijssen Experimentation :) \$\endgroup\$
    – Gymhgy
    Jan 30, 2019 at 16:19
  • \$\begingroup\$ github.com/dotnet/roslyn/pull/5857 \$\endgroup\$
    – dana
    Jan 30, 2019 at 20:31
  • 2
    \$\begingroup\$ "Command-line options are not included in the byte count of your program" [citation needed] \$\endgroup\$ Apr 17, 2019 at 7:04
  • \$\begingroup\$ @PeterTaylor Added relevant link to meta \$\endgroup\$
    – Gymhgy
    Apr 18, 2019 at 2:25
1
\$\begingroup\$

Use dynamic instead of types with longer names in function declarations

For example, instead of
DateTime f(int y)=>
use
dynamic f(int y)=>

\$\endgroup\$
2
  • \$\begingroup\$ Why wouldn't I just use var here? \$\endgroup\$
    – jcolebrand
    Jan 8, 2020 at 17:38
  • \$\begingroup\$ @jcolebrand Clarified. \$\endgroup\$
    – Adám
    Jan 8, 2020 at 17:40
1
\$\begingroup\$

The first and third sections of for can contain multiple comma-separated statement expressions, including assignments (but not declarations) and method calls:

for(i=0,k=1;i<n;Console.WriteLine(k),k*=3)i++;
\$\endgroup\$
1
2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.