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 longer
It 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.