Good question!
I asked the same thing a while back.
Basically, it's a speed/memory trade-off. The benefit of caching string hash codes is arguably outweighed by the overhead of every single string object requiring another 32 bits of memory to be allocated. This makes sense when you think about the large number of strings that may exist in a program versus the number whose hash codes you care about (presumably because you're using them as keys).
The latter number may be large in some programs, but it may also be quite small. It could even be zero in a lot of cases.
If performance were an extreme concern for you in certain scenarios, you might consider writing your own wrapper that does cache its hash code:
public class StringKey
{
string value;
int hashCode;
public StringKey(string value)
{
this.value = value;
this.hashCode = value.GetHashCode();
}
public override int GetHashCode()
{
return this.hashCode;
}
public override string ToString()
{
return this.value;
}
// Plus all the other stuff you'd want to include here,
// e.g., Equals, CompareTo, etc.
}
To get any benefit out of this, of course, you'd still need to be very careful to reuse these StringKey
objects basically throughout your program everywhere. In the vast majority of cases, this would not be worth the effort; I've included the idea only as something to consider if you happen to be an exceptional case.