3

How to access indexers in switch expression? There is a nice property pattern syntax in switch expressions, but I can't figure out or find any information about using indexers.

Given following code:

var a = "123";
if(a.Length == 3 && a[0] == '1')
    Console.WriteLine("passed");

How do to convert it to switch expression? Matching a.Length is easy, but how to specify a match for a[0] == '1'?

var b = a switch
{
    { Length: 3, this[0]: '1' } => "passed", // CS8918: Identifier or a simple member access expected.
    _ => "error"
};
Console.WriteLine(b);

Fiddle.

Sinatr
  • 20,892
  • 15
  • 90
  • 319
  • 5
    => https://dotnetfiddle.net/CeqiOG ? `['1', _, _] => "passed"` == exactly 3 elements, of which the first is '1' and the other two I don't care about. But that's _not_ using indexers, of course. – Fildor Jul 20 '23 at 08:21
  • 4
    You can use a [list pattern](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns#list-patterns) with containers but this is really string matching. You can put that condition in a `when` clause. `{Length: 3} s when s[0]=='1'`. Or use a regular expression – Panagiotis Kanavos Jul 20 '23 at 08:22
  • Are you trying to create a parser? – Panagiotis Kanavos Jul 20 '23 at 08:24
  • @Fildor-standswithMods, smart, so the `Length` is also implicitly checked. – Sinatr Jul 20 '23 at 08:29
  • Yes. But I somehow missed, that you _want_ to use an indexer. – Fildor Jul 20 '23 at 08:32
  • @PanagiotisKanavos, not a parser, just simplifying some code (multiple `if/else`) and learning new stuff. Solution with `when` works perfectly, thanks. – Sinatr Jul 20 '23 at 08:32
  • 1
    Requiring a fixed length with a specific first letter sounds a lot like part of a mini parser. You could come up with simpler and faster code if you look at the overall syntax and use some parser techniques. – Panagiotis Kanavos Jul 20 '23 at 08:38

3 Answers3

4

One way to do it is by using when statement. It is not as beautiful as property pattern, but can be a workaround:

var b = a switch
{
    { Length: 3 } when a[0] == '1' => "passed",
    _ => "error"
};
Dmitrii Dovgopolyi
  • 6,231
  • 2
  • 27
  • 44
  • I forgot to mention that I am limited by C# 9.0 atm. I am going to use your solution. `['1', _, _]`-solution requires at least 11.0. =/ – Sinatr Jul 20 '23 at 08:36
4

Since C# 11 you can use list patterns:

var b = a switch
{
    ['1', _, _] => "passed", // CS8918: Identifier or a simple member access expected.
    _ => "error"
};
Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • 1
    I like this answer the most, makes code very clean. Also flexible, to example: `['1', _, _] or ['1', _] when a[1] == '2' => "passed"` . But it's too specific to `Length` check and I want to stay on topic, just to use indexers is done by using `when`. – Sinatr Jul 20 '23 at 10:03
  • 1
    @Sinatr @Sinatr the second one would be simply `['1', '2']` or just `"12" => ...`, if you want check with any length then you can do `[_,'2', ..]` - this will check for string with 2 in second position and length >= 2. – Guru Stron Jul 20 '23 at 10:30
1

Since c# 8.0. You can acheive it with switch expressions:

var a = "123";
var tuple = (a.Length, a[0]);
switch (tuple)
{
    case (3, '1'):
        //do something
        break;
    case (4, '2'):
        //do something
        break;
}

or

string b = (a.Length, a[0]) switch
{
    (3, '1') => "passed",
    (4, '2') => "failed",
    _ => "error"
};

You can find more usage of switch expressions from this documentation (https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/switch-expression)

Edit: In a case where you need to check a.Length before evaluating a[index]. You can actually check it when creating a new Tuple.

var a = "323";
var index = 5;
char? ch = (a.Length >= index + 1) ? (char?)a[index] : null;
var b = (a.Length, ch) switch
{
    (_, null) => "error",
    (3, '1') => "pass",
    _ => "default"
};
System.Console.WriteLine(b??"");
Magic1647
  • 143
  • 6
  • Great idea to prepare data before entering `switch`, but this is not as flexible as `when`-solution and has an overhead when matches don't need such parameters. What if I also need to check `a[1]`? – Sinatr Jul 20 '23 at 09:05
  • 1
    This will throw an IndexOutOfRangeException `a` is an empty sting – Klaus Gütter Jul 20 '23 at 09:07
  • @KlausGütter, aye, no [short-circuit-evaluation](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/boolean-logical-operators#conditional-logical-and-operator-) when preparing data like this too. – Sinatr Jul 20 '23 at 09:17
  • @Sinatr I have edited my answer, is this the one you are looking for? – Magic1647 Jul 20 '23 at 11:07