C sharp
Basics
Section titled Basics- C# is open-source. If you ever want to find the code behind a particular class, go to a page like this and click the “Source” link toward the top:
null!
: the “null-forgiving” operator (reference). Use it when you know that an expression can’t benull
but the compiler can’t tell. For example, in Godot, you can set some variables through the game engine itself, but C# won’t know that they’re set, so you would initialize the variable usingnull!
:- Note that structs and classes have different ways of becoming nullable. Instances of a class can always be
null
, but instances of a struct need to be wrapped inNullable<T>
, which is what happens when you make aFooStruct? myStruct
. When wrapping in aNullable
, you should useHasValue
andValue
and not just!= null
andmyStruct!
.
- Note that structs and classes have different ways of becoming nullable. Instances of a class can always be
- The equivalent of Java’s
IllegalStateException
isSystem.InvalidOperationException
(reference). - Use a
Stopwatch
to keep track of how much time has elapsed:
Making a new project from VSCode
Section titled Making a new project from VSCodeYou actually just do this from the command line using the dotnet
tool (reference):
dotnet new sln -o MyProject
dotnet new console -o MyProject.Main
dotnet sln MyProject.sln add MyProject.Main/MyProject.Main.csproj
dotnet build
dotnet run
- It’ll show “Hello, World!”
See C sharp and JSON.
Using Linq in a debugger
Section titled Using Linq in a debuggerAt least in VSCode, if you try to evaluate an expression like foo.ToList()
in a file that didn’t explicitly write using System.Linq;
, you’ll get an error like this:
You can still use it via System.Linq.Enumerable.ToList
, which is a bit unwieldy: System.Linq.Enumerable.ToList(foo.Values)
Generics
Section titled GenericsIf you have a generic class and you want to get the name of that class with a specific type in it, you can’t just do nameof(GenericClass
) or else you get something like GenericClass`1
. Instead, you have to do this: typeof(GenericClass<SomeTypeHere>).Name
.
Tuples
Section titled TuplesIf you need an arbitrary bunch of parameters (e.g. a Pair
of something), try Tuple
:
- Anonymous:
Tuple<X, Y>
- Named:
(X x, Y y)
The named variant is really nice for saying what something represents, that way you don’t just have Tuple<int, int>
and wonder which int
is what:
This applies even to iterating over dictionaries:
Events
Section titled EventsSimple example:
Using reflection to call a generic function at runtime (reference)
Section titled Using reflection to call a generic function at runtime (reference)Here’s an example:
Note that this may not work on all platforms (for example, maybe iOS) since it requires JIT compilation.
Troubleshooting
Section titled TroubleshootingUsing Linq with enums
Section titled Using Linq with enumsConsider this code:
The issue is that the default for an enumeration is the 0 value of that enumeration, which in this case is Apple
(but if that weren’t defined, it would literally be the value 0
). Your options are:
- Manifest
None = 0
in theFruit
enum - Change
Apple
to beApple = 1
and explicitly check forfruit == 0
- Write your own version of
Find
orFirstOrDefault
(probably as an extension). - Change
new List<Fruit>()
→new List<Fruit?>()
- (this probably isn’t a great option because you’d realistically have to change practically every type in the call stack to get to whichever function you’re writing)
Anonymous lambdas capturing the wrong value
Section titled Anonymous lambdas capturing the wrong value(kw: closure)
Example program:
The output of this program is 10 10 10 10 ...
and not 0 1 2 3 4 5 ...
. To fix this, simply copy i
into a local variable and use that: