If you're a self-taught programmer, skim a good textbook. But you're not going to do that and most textbooks are bad anyway. So what you really need is just a list of some possible holes in what you know. Here's a quick list:
Math with double
(or float
) can be off by a tiny amount. For example, 1.7 + 2.3 may not be exactly 4.
That won't matter most of the time, since the off-by is super super small. But it's a problem for exact compares. This next thing adds 0.1 over and over, forever, since it never hits 1.0 exactly:
double x=0.1; while(x!=1.0) x+=0.1f;
When it should logically be 1.0, it will really be 0.999999 or 1.00001. To fix stuff like this, we do stuff like while(x<=0.999)
.
if
'sif
's can be on one line with no curly-braces:
if(score<0) score=0;It only works for single statements. As soon as you have 2 or more, you need the curly-braces.
//n = [old formula] // did this instead of deleting n = [new formula]
if(cats>10) { int extraCats=(cats-dogs)/2; // used in this IF ... } // extraCats is gone
%
gives the remainder. 9%4
is 1 (4 goes in twice, with 1 left over). Common uses are if(n%2==0)
checks for even numbers (dividing by 2 has remainder 0) and n%10
gives the 1's place.
3 * 2+5
is 6+5, the same as normal math. < and > naturally go after math, and && and || naturally go after those. People will add extra parens to make it look nice, but they aren't required.
Dictionary<string,double> ToolCost = Dictionary<string,double>; ToolCost["hammer"]=6.8; double costWithTax = ToolCost["wrench"]*1.2f;
These are things that the internet might shove at you, but aren't worth the trouble to learn. Some are special-purpose and you won't need them. Others don't do anything you can't already do.
public int n { get; set; }
. A regular public variable: public int n;
, is the same thing but easier.
int
.
switch
'sfor
and while
loops do everything you need.
if(n==1) print("tiny"); else if(n==2) print("small"); else if(n<=4) print("medium"); else if(n<=7) print("big"); else print("huge");It quits when it finds a match, allow easy checking for ranges ("medium" is 3 or 4), and allow an optional "none-of-the-above" at the end.
bool
typebool
is a true/false type. The technical name is boolean
. It can be used inside of if
's, allow you to compute long conditions in steps:
bool is1to10 = n>=1 && n<=10; bool isWholeNumber = (int)n == n; if(is1to10 && isWholeNumber) ...Notice how we didn't need
==true
, just if(is1to10)
is good enough.
bool
. It's called a flag after an old-style mailbox up/down flag. There's nothing special about a global true/false. If you want to remember whether a light is on, an int
will work (0=off, 1-on), but bool
's can look nicer.
A[i]
, can't be out-of-range):
if(i>=0 && i<A.length && A[i]==3) // safeWhen
i
is too big or small, it quits immediately. The trick also works for OR's, but in a different way.
// between 1 and 10: if(n>=1 && n<=10) // not between 1 and 10: if(n<1 || n>10)This is called DeMorgan's law if you want to look it up.
if(!w.Contains("rat")) // words without "rat" if(w.Contains("rat")==false) // same, but a little longerIt can be is a safe way of flipping something complicated. for example "n is not 2, 7 or 9":
if( !(n==2 || n==7 || n==9) )
foreach
loop is great for quickly looking through an array, but whenever you're stuck trying to do something tricky with a foreach
, go back to a regular old index loop. Suppose we need to skip the first item. A for-loop can simply start at 1:
// check everything _except the first item_: for(int i=1; i<A.length; i++) ... // ^ one, not zero
done
boolean can often help. The template looks like this:
bool done=false; while(!done) { if( [something] ) done=true; if( [something else] ) done=true; }That lets you assemble when to quit over as many lines as you want, instead of having to cram it all into the
while
parens.
break;
instantly quits a loop. It's good for "find one thing in a list" loops. continue;
skips to the end of the loop, but then keeps looping. It's good for "don't count this one, but keep going".
while( [something] ) { ... if( [something else] ) continue; // go straight to next item if( [a third thing] ) { [do some stuff]; break; } // quit the loop ...
break;
. It can handle the really weird loop problems:
while(true) { // never quits? But quits w/break, inside if( [out of options] ) break; ... if( [found item] ) break; ... }
break
quits immediately, which can be nicer than if(...) done=true; else
.
// 1st attempt, names of 3 people: string fname1, lName1; string fname2, lName2; string fname3, lName3;You'll be making lots of them, and first and last name always go together, so a class is a nice shortcut:
class fullName { public string first; public string last; } name1 = new fullName(); // creates first and last, glued together name2 = new fullName(); // ...and so on
class fullName { public string first, last; public void set(string fName, lName) { // might be useful first=fName; last=lName; } public string asString() { return last+", "+first; } }It's still not some big fancy class -- it's just
n.first
and n.last
with 2 shortcuts. name1.set("Gar", "Reblo");
is a little nicer than name1.first="Gar";
... .
// number of times we rolled that number: int d1, d2, d3, d4, d5, d6;We can make the same thing (6 int variables) with one size-6 array:
int[] D=new int[6]; // 6 integer variablesWe can set them all to 0 with a loop:
for(int i=0;i<6;i++) D[i]=0; // reset all to 0Being able to use 0-5 to "look-up" a box is great. Suppose
n
is the number we just rolled. D[n-1]++;
adds 1 to the it's total (in other words, if we roll a 2, that short line adds to the 2's count). Without an array, we'd be stuck using 6 if's.
string[] catNames = new string[20]; int[] catAges = new int[20]; double[] catWeights = new double[20];That works, cat#7 is spread out among the slot#7's of the name, age and weight arrays. But it might be nicer to make a Cat class and have one array of that:
Cat[] = new Cat[20]; for(int i=0;i<20;i++) Cat[i]=new Cat(); Cat[3].age=5; // using it
List<int>
instead of int[]
For organization, built-ins are divided among folders, called namespaces
. System
is a main one. Inside it are namespaces IO
, for files and Random
and so on. To find the file-reading class we use System.IO.FileStream
. So far, so good.
c1.claws.sharpness
uses dots to look inside of object c1
. Dots are used with classes. But dots are also used with namespaces. Hmmm... . The two things are different, and it's a little confusing we use the same symbol for both things.
System.IO
every time is a pain. using System.IO;
adds a shortcut to leave it out. It's common to have tons of using's at the top of a program. Nothing bad happens if you have ones you don't need.BugMachine.Useful.Bee
to get the bee class. As usual, you can add using BugMachine;
to save typing later.System.IO.Directory
holds normal functions for playing around with files, but Directory
says it's a class! The thing is, it's not a class. It's marked as static
, which means it counts as a namespace.static functions
if it is. Suppose getRandomCat is static in the Cat class. You'd call it like Cat c1=Cat.GetRandomCat();
, using Cat as a namespace. To compare, Cat.claws
is an error if claws isn't static -- you'd need to have a real cat.
To sum up: remember that dots can be class member-dots, or they could be namespace path-dots. And that some functions in a class are just normal if you see static
by them.
Program speed-up tricks don't do any good. Most don't work, some make the program slower, and the rest are a super-tiny speed-up. You're basically wasting time and causing bugs for no reason.
For example, it seems as if you could take lots of little steps and combine them into a single, faster equation. Nope. The compiler uncombines them. It even adds back temporary variables which you eliminated. Attempts at little speed-ups are called micro-optimizations if you want to look it up.
The one place to worry about speed is avoiding extra loops. Take this sample code:
foreach(f in Frogs) { Frog bestFrog = findBestFrog(); if( [something with bestFrog ) ... ... }
The problem is that findBestFrog
is probably a loop through every Frog. It's a nested loop. With 1,000 frogs we have a million steps total (1,000 times 1,000). If we know the best frog doesn't change here then we could compute it ahead-of-time, removing the nested loop for a big speed-up:
Frog bestFrog = findBestFrog(); for(each(f in Frogs) { if( [something with bestFrog] ) ... ... }
Big-O: built-in functions (usually for arrays) tell you their nested loops using "big-O" notation. Roughly: O(1) means no loops, O(n) means a loop, and O(n^2) means a nested loop.
Sometimes this can be useful. For example, removing from the front of a List is O(n) -- a loop -- but removing from the back is only O(1) -- not a loop. If you can turn your front-removes into back-removes, you get a big speed-up.
Linked-list class: a linked-list does the same thing as an array (or a List). It just holds a list of items. But it works differently, making it a lot faster for some special things.
In a linked-list, each item has a link to the items that come before and after. This makes it fast to add or delete from anywhere; but takes extra space and makes it much longer to just jump to any box. For super-intense code on a big list where you'll mostly add and remove from all over, a linked-list can be a huge speed-up. But otherwise, just use an array or List.
References are used in 2 different ways, which isn't really explained. 95% of the time they're just variables. Here c1 and c2 are just cats:
Cat c1 = new Cat(); c1.name="Ally"; Cat c2 = new Cat(); c2.name="Bear";
But we also use them as "pointers". In our minds, activeCat
isn't a cat. It's job is to point to some other cat:
// activeCat will point to some real cat: Cat activeCat = c1; if( [something] ) activeCat=2;
Here's an example of the same thing with arrays. AllDogs is an array of 20 real dogs:
Dog[] AllDogs = new Dog[20]; for(int i=0;i<20;i++) AllDogs[i]=new Dog();
We'll select from these for 6-dog sled teams. SledTeam1
is an array of 6 pointers to dogs in the AllDogs
array:
// these will only point to things in AllDogs: Dog[] SledTeam1 = new Dog[6]; // no dogs on the team, yet. Set pointers to null: for(int i=0;i<20;i++) AllDogs[i]=null; // choose some dogs onto the team: SledTeam1[0]=AllDogs[3]; SledTeam1[1]=AllDogs[16];
You can't tell from how they're declared -- both are Dog[]
dog-arrays. You have to figure out real-or-pointer? from how they're used.
C# has a value-type version of classes, named struct
. Classes do everything you need, and work better. You never need to create a struct, but you may have to work with them.
You can never have a reference to a struct. Assignment statements make copies, the same as int's or strings. w2=w1;
copies. w2
is still a different thing than w1
.