Avoid allocating Regex when using static methods (#496)
* Avoid allocating Regex when using static methods
Regex supports a cache that's meant to be used for the static methods, e.g. the static Regex.IsMatch. However, currently that cache isn't caching the actual Regex class, but rather a sub object that stores some of the state from the Regex and then is used to rehydrate a new Regex instance. This means we allocate a new Regex object when these static methods are used, even if the data is found in the cache. This means an operation like the static Regex.IsMatch is never allocation-free. There's also weirdness around this caching, in that when a new Regex instance is constructed, it checks the cache (paying the relevant lookup costs) even though it doesn't add back to the cache, so the only way it would ever find something in the cache is if the same set of inputs (e.g. pattern, options, timeout, etc.) are used with both the Regex ctor and with the static methods, where the static methods would populate the cache that's then consulted by the ctor. This adds unnecessary expense on the common path for a very uncommon situation; it also complicates the code non-trivially.
This commit changes things to cleanly separate the cache from Regex, and to actually cache the Regex instance itself.
* Address PR feedback