Improve performance of Regex ctor and IsMatch

The Regex class maintains a cache of byte codes, which the Regex ctor indexes into using a key.  It uses this seemingly innocuous line to create that key:

String key = ((int)options).ToString(NumberFormatInfo.InvariantInfo) + ":" + cultureKey + ":" + pattern;

This, however, has the unfortunate effect of allocating a string for the options, a string array for the five strings to be passed to the String.Concat call generated by the compiler, another string array allocation inside of Concat, and then the resulting string for the whole operation.  The cost of those allocations is causing a non-trivial slowdown for repeated Regex.IsMatch calls for simple regular expressions, such as for a phone number (e.g. "^\\d{3}-\\d{3}-\\d{4}$").

This commit adds a new struct key type that just stores the constitutent options, cultureKey, and pattern, rather than creating a string to store them.  That key is then what's stored in each entry in the cache.

For repeated Regex.IsMatch calls for basic regular expressions like the phone number one previously mentioned, on my machine this improves throughput by ~35%, in large part due to ~80% reduction in number of allocations, and (for this particular test case) an ~70% reduction in number of bytes allocated (it depends primarily on the length of the pattern and the length of the culture name).
Merge pull request #231 from stephentoub/regex_ctor_perf

Improve performance of Regex ctor and IsMatch