Description
Following patterns are based on the syntax proposed in #206.
List Patterns
List patterns can be used on arrays and all types that have implemented the IList
interface . It would iterate through the list and match the list-subpatterns.
Syntax
list-pattern:
{
list-subpatterns}
{
}
list-subpatterns:
list-subpattern
list-subpatterns,
list-subpatternlist-subpattern:
pattern
sequence-wildcard-patternsequence-wildcard-pattern:
**
***
Remarks
{ }
matches an empty list. Determined byEnumerable.Any
orICollection.Count
based on type.**
matches any sequence of one or more items.***
matches any sequence of zero or more items.- To keep the rules simple, sequence wildcards can be used only one time in the whole pattern.
- At runtime, considering sequence wildcards, it first calculates the minimum/maximum/exact length of the list, if it matches then it starts to iterate.
EDIT 1: Sequence wildcards are capable to be used also in tuple patterns.
switch(tuple) {
case (true, **): ...
}
It would match a tuple with any length equal or greater than two.
EDIT 2: It would be nice to be able to use slices (#120) to catch the "rest" of the list at once, e.g.
arr is { int first, .. int[:] rest }
// equivalent to
if(arr is { int first, *** }) {
rest[:] = arr[1:]; // skips the first item
}
Examples
switch(array) {
// matches an empty array
case { }:
// matches an array with exact length of two
case { var i1, var i2 }:
// matches an array of tuples with minimum length of two and returns the first two item
// with the first item deconstructed into t1 and t2
case { (var t1, var t2) , var i2, *** }:
// matches an array with the minimum length of three,
// picks the last three item and skips the one before the last
case { *** , var i1, *, var i2 }:
// matches an array with the minimum length of three, returns the first and last item
case { var i1, ** , var i2 }:
}
Indexer Patterns
Indexer patterns can be used on all types with an indexer, just like dictionary initializers.
Syntax
indexer-pattern:
{
indexer-subpatterns}
indexer-subpatterns:
indexer-subpattern
indexer-subpatterns,
indexer-subpatternindexer-subpattern:
[
constant-expression-list]
is
pattern
An indexer pattern matches the values extracted by the use of indexer with the specified arguments. Following the property pattern syntax, we use an is
keyword instead of =
sign. The whole match fails only if the one of indexer subpatterns fail. The order in which subpatterns are matched is not specified, and a failed match may not match all subpatterns. It's a compiler-error if type of the expression _e_ have not defined an indexer with parameters of type constant-expression-list, hence it's pattern incompatible.
EDIT: The indexer pattern syntax can be mixed with property-pattern so one can check a indexer and a property in the same pattern (analogous to object initializers).
String Patterns
String patterns can be considered as inverted string interpolation, and of course they can be combined with regular expression literals (#5806):
var str = "str123";
if(str is ~$"[a-z]+{int number:\d+}") {
Debug.Assert(number == 123);
}
Behind the scene, this creates a regular expression group at the interpolating section ({ }
) and applies the formatting part (\d+
) to it, when the regex succeed, then it tries to convert the group capture via TypeConverter
back to the variable type. If regular expression doesn't match or conversion wasn't successful, pattern fails.
Default type for variables in string patterns is System.String
. The syntax for string patterns is pretty much the same as string interpolation, but formatting part after colon has a different meaning. This can be extended to bind repeating captures to an array or IEnumerable
.