It also makes a difference on the calling side, however. Out parameters don't need to be initialized before passing them to a function:

void Test()
{
int n;
SomeFunc(ref n); // This won't compile; n is uninitialized.
SomeOtherFunc(out n); // Whereas this is fine, even if n is uninitialized
// n is guaranteed to have a value after the call to Foo, because
// the function is required to assign it a value before returning.
}

Basically, use ref when you need to pass a value in and get a different value out, and use out when you only need to get a value out.

In general, try to avoid using ref/out parameters, because they lead to code that doesn't compose well. Sometimes it's unavoidable, but always try to find an alternative solution.

Just in case (since this is a common point of confusion): you do not need to use ref to be able to modify a parameter of a reference type (class) inside a function. With reference types you already passing a reference. Passing a reference type with ref means you pass a reference to a reference, which is only needed if you want to return a completely different instance to the caller.

class Foo
{
public string Bar { get; set; }
}
void ModifyFoo(Foo value)
{
value.Bar = "hello"; // The caller will see this change, even without ref
value = new Foo(); // But the caller will *not* see this change; it doesn't get the new instance.
}
void ReplaceFoo(ref Foo value)
{
value = new Foo(); // With ref, the caller will see this change and get the new instance.
// I could've used out here, since we don't use the value that was passed in.
}