URL pattern matching for request blocking
General considerations

You are writing a "denylist". There's some clean and simple denylist rules that can help rule out a lot of things already. One of these rules is denying URLs based on their scheme alone.

A few examples for schemes that you may not have considered:
steam://, file://, sftp://, chrome://, about:// 

Most of these are URLs you most likely want to block. In that case "inverted matching" behaviour is really useful.



Furthermore denylists usually tend to be used for "overblocking out of caution". I'm not sure how that's for your exact use-case, but if you want that to happen, making your patterns as general as possible is a great idea :)



URI vs. URL:
Something to additionally keep in mind here: URI stands for "Uniform Resource Identifier" and URL for "Uniform Resource Locator". Your code specifically exposes a Uri, not a Url overload for matching. This implies that one should be able to blacklist things like the following URIs:

git@github.com:username/repository.git
mailto:username@example.com
irc://#c-sharp@freenode


If your class is only intended to deal with URLs, you should only expose an overload for Url :)

Personal preferences

I personally very much prefer to use factory methods over constructors to validate arguments. YMMV, but I expect constructors to not throw exceptions.

I also prefer to add braces wherever they delimit a block. I can understand the omission for the argument checking though.

Implementation considerations

I like that you're using a lot of intermediate variables. Some minor naming nitpicks could be had there, but I think this code will get the most benefit from using tuples:

var (scheme, hostAndPath) = SplitSchemeHost(pattern);
var (host, path) = SplitHostPath(hostAndPath);


This allows you to push the index matching behaviour into these methods and keep it out of the constructor. (Minor nitpick: 3 is a magic number in the hostAndPath creation)

Aside from that this code is very clean already. I'd maybe not create the patterns within the interpolated strings, but given the simplicity here it seems appropriate to do so.