15

Help please, I have this case:

switch(MyFoo()){
    case 0: //...
        break;
    case 1: //...
        break;
    case 2: //...
        break;
    default:
        // <HERE>
        break;
}

As you can see the switch gets the value directly from a method without saving it as a variable.

Is it possible to get which value fires the default case? For example if MyFoo() returns 7, how can I get that value?

I want to avoid to save the method result as a variable, is there a way to get the switch value from inside a case? Something like this:

default:
    this.SwitchValue // <<--
    break;

Thank you for reading, ~Saba

Sabaz
  • 4,794
  • 2
  • 18
  • 26

5 Answers5

15

Is there a way to get the switch value from inside a case?

The only (proper) way is actually to store the result of MyFoo() in a variable.

var fooResult = MyFoo();
switch (fooResult)
{
    case 0:
        ...
        break;
    ...
    default:
        handleOthersCase(fooResult);
        break;
}

This code is readable and understandable and have no extra cost (As @SheldonNeilson says: It's on the stack anyway).

Also, the MSDN first example about switch totally look like this. You can also find informations int the language specification.

You also can make your own switch based on a dictionary, but the only advantage I see is that you can use it for complex cases (any kind of object instead of string/int/...). Performance is a drawback.

It may look like this:

public class MySwitch<T> : Dictionary<T, Action<T>>
{
    private Action<T> _defaultAction;

    public void TryInvoke(T value)
    {
        Action<T> action;
        if (TryGetValue(value, out action))
        {
            action(value);
        }
        else
        {
            var defaultAction = _defaultAction;
            if (defaultAction != null)
            {
                defaultAction(value);
            }
        }
    }

    public void SetDefault(Action<T> defaultAction)
    {
        _defaultAction = defaultAction;
    }
}

And be used like this:

var mySwitch = new MySwitch<int>();

mySwitch.Add(1, i => Console.WriteLine("one"));                             // print "one"
mySwitch.Add(2, i => Console.WriteLine("two"));                             // print "two"
mySwitch.SetDefault(i => Console.WriteLine("With the digits: {0}", i));     // print any other value with digits.

mySwitch.TryInvoke(42);                                                     // Output: "With the digits: 42"

Or based on this response, this:

public class MySwitch2<T>
{
    private readonly T _input;

    private bool _done = false;

    private MySwitch2(T input)
    {
        _input = input;
    }

    public MySwitch2<T> On(T input)
    {
        return new MySwitch2<T>(input);
    }

    public MySwitch2<T> Case(T caseValue, Action<T> action)
    {
        if (!_done && Equals(_input, caseValue))
        {
            _done = true;
            action(_input);
        }
        return this;
    }

    public void Default(Action<T> action)
    {
        if (!_done)
        {
            action(_input);
        }
    }
}

Can be used like that:

MySwitch2<int>.On(42)
    .Case(1, i => Console.WriteLine("one"))
    .Case(2, i => Console.WriteLine("two"))
    .Default(i => Console.WriteLine("With the digits: {0}", i));
Community
  • 1
  • 1
Orace
  • 7,822
  • 30
  • 45
  • 1
    Wouldn't it be nicer if you could do the following so that fooResult is only visible in the scope of the switch block? switch (var fooResult = MyFoo()) { case 0: ... break; ... default: handleOthersCase(fooResult); break; } – George Birbilis Sep 01 '15 at 17:30
  • ...if you agree, please vote at https://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/9578766-define-variable-in-switch – George Birbilis Sep 01 '15 at 18:18
  • The problem is just that a `goto case` statement that refers to an undeclared case and therefore will go to the `default` will not pass the case label value because it's not the value of the case variable. – Davide Cannizzo Jan 10 '18 at 16:25
5

I can't see a reason as well why to use it like that but may be a work around will be like this:

int x;
switch ( x = MyFoo())
{
    case 0: //...
        break;
    case 1: //...
        break;
    case 2: //...
        break;
    default:
        var s = x; // Access and play with x here
        break;
}
Amr Elgarhy
  • 66,568
  • 69
  • 184
  • 301
3

No, this isn't possible. You can assign the value to variable inside switch, if you want to look like reinventing the wheel:

        int b;
        .....
        switch (b = MyFoo())
        {
            case 1:
                break;
            case 2:
                break;
            default:
                //do smth with b
                break;
        }
Andrew
  • 3,648
  • 1
  • 15
  • 29
1

The easiest way is to save the result of MyFoo() as a variable.. But if you don't want to do that you could do:

switch(MyFoo()){
    case 0: //...
        break;
    case 1: //...
        break;
    case 2: //...
        break;
    default:
        this.SwitchCase = MyFoo();
        break;
}

Although I would advise against this and say save the value as a variable to save your program the extra work.

Saving the value of MyFoo as a variable becomes more important the more complex the example gets as the value of MyFoo could have changed between the switch and default case.

Also this will only work where MyFoo has no side-effects and obviously must always return the same value for any given parameter.

for example the following would work:

Private int MyFoo()
{
   return 3;
}

But the following would not:

private int MyFoo()
{
  Random r = new Random();
  return r.Next(5);
}
StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
JKennedy
  • 18,150
  • 17
  • 114
  • 198
  • I don't think the edit here makes the answer any clearer, the concept of a pure function is clearer than the replacement text in my opinion. – Lasse V. Karlsen May 06 '20 at 13:59
1

This is possible now.

https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#property-patterns

Example:

int? TryGetColumnIndex(string columnName)
    => GetValue(columnName)
    switch
    { var result when result > -1 => result, _ => new int?() };

result will capture the result of GetValue.

Even cooler, you can do propery checks.

i.e instead of when result > -1 you can even say when result.ToString().Length > 2 and such.

user3265356
  • 49
  • 1
  • 5