0

I use C# pattern matching often in my work, especially with the switch/case as illustrated in the example below. These work great and no issues here as long as the type is passed within the ViewModel as shown:

var fruit = new Apple();
MakePie(fruit);
.
private void MakePie(object fruit)
{
    if (fruit != null)
    {
        Debug.WriteLine(fruit.GetType().Name);
        switch (fruit)
        {
            case Apple _:
                MakeApplePie();
                break;

            case Cherry _:
                MakeCherryPie();
                break;
        }
    }
}

However, I discovered that if I pass the type as a CommandArgument from XAML, the above switch/case will fail, even though the correct type is passed as the argument -- it just that the switch/case no longer evaluates correctly by C# pattern matching for some reason.

<Button Content="Make Apple Pie" CommandParameter="{x:Type models:Apple}" Command="{Binding MakePie}"/>
<Button Content="Make Cherry Pie" CommandParameter="{x:Type models:Cherry}" Command="{Binding MakePie}"/>

So just to perform a simple determination, I used this:

Debug.WriteLine(fruit.GetType().Name)

I was expecting to see either 'Apple' or 'Cherry', but instead, I got 'RuntimeType' as the name, even though I can clearly see from the fruit object that its type and type name match correctly to the 'Apple' or 'Cherry' classes from my models.

So can anyone explain or tell me if I am doing something wrong, or that I simply cannot pass the type from XAML to a switch/case pattern matching unless it is done from the ViewModel?

What would be the correct usage/implementation if you want to pass a type as a CommandArgument from XAML and also want the switch/case pattern matching to work?

To Amjad and dba:

Here, try the codes below on your end and let me know your results. Notice the private string TestSwitch(object t) function/method takes an object t as the argument. You will see that the switch pattern matching works just fine with t being an object. This is how I have implemented this pattern numerous times in other applications, but have always done it inside the ViewModel. First time trying to do it in XAML (no changes other than where the type is passed), and it doesn't work, and I can't seem to figure out the reason.

    public partial class MainWindow : Window
    {
        public class TestClass1
        {
            public int MyProperty1 { get; set; }
        }

        public class TestClass2
        {
            public string MyProperty2 { get; set; }
        }

        public class TestClass3
        {
            public string MyProperty3 { get; set; }
        }

        public MainWindow()
        {
            InitializeComponent();

            var t1 = new TestClass1();
            Debug.WriteLine(TestSwitch(t1));

            var t2 = new TestClass2();
            Debug.WriteLine(TestSwitch(t2));

            var t3 = new TestClass3();
            Debug.WriteLine(TestSwitch(t3));
        }

        private string TestSwitch(object t)
        {
            switch (t)
            {
                case TestClass1 _:
                    return "TestClass1";

                case TestClass2 _:
                    return "TestClass2";

                case TestClass3 _:
                    return "TestClass3";
            }
            return "??????";
        }
    }

Ari Pub
  • 11
  • 4
  • what will fail? will it even hit the function or switch? – Amjad S. Feb 01 '22 at 18:49
  • Amjad: Yes, it hit the method/function and the switch just fine and the type is also passed correctly as the CommandParameter. But for each case statement, which used to work when the type was passed from within the ViewModel, now does not evaluate correctly so naturally, it exits the function without doing anything. – Ari Pub Feb 01 '22 at 18:55
  • it wont work because you need to cast the object fruit to the type u want to compare too – Amjad S. Feb 01 '22 at 19:29

3 Answers3

1

I had a brain lapse from staring at the codes working long hours so I found the reason the switch failed and have since then refactored so things work correctly.

Basically, you can't use C# type-based switch pattern matching feature when you don't have an actual instance of the switched-upon type available, and instead, only have its System.Type, which is what I was passing from XAML.

So the solution is to either pass an actual instance of the type as an object type and things will work as expected, or use an alternate implementation something like the below:

var type = parameter as Type; //where parameter is the passed argument of object type
switch (type)
{
    case Type _ when type == typeof(Apple):
        MakeApplePie();
        break;

    case Type _ when type == typeof(Cherry):
        MakeCherryPie();
        break;
}
Ari Pub
  • 11
  • 4
0

Try please my method :

Xaml:

<Button Text="Make Apple Pie"  Command="{Binding TestComand}"  CommandParameter="Apple" />
 <Button Text="Make Cherry Pie" Command="{Binding TestComand}"  CommandParameter="Cherry" />

ViewModle

 public class fruitVM
    {
        public Command TestComand { get; set; }
        public fruitVM()
        {
            TestComand = new Command<object>(func);
        }

        private void func(object obj)
        {
            string x = obj as string;
            switch (x)
            {
                case "Cherry":
                    break;

                case "Apple":
                    break;

            }
        }
    }

 
Amjad S.
  • 1,196
  • 1
  • 4
  • 16
  • Hi Amjad: I appreciate your input and suggestions, but I know how to switch using many different methods by using the class type Name and string as the cases. But as stated at the start of this post, I have used the switch pattern matching as shown in the example in many applications with no problems -- meaning the function/method takes the argument passed as an **object** (because it can be of any type) and the switch will correctly evaluate the type of the object as long as I do it in the ViewModel. But if I pass the type to the function from XAML, it doesn't work. – Ari Pub Feb 01 '22 at 23:29
  • Amjad: Please see edited post above. Thanks. – Ari Pub Feb 01 '22 at 23:40
  • Amjad: Upon closer review of what I did in previous applications, I refactored my implementation for this application using a slightly different approach while retaining the same switch pattern matching by type implementation I had done previously while still being able to pass the type from XAML. Thank you for your inputs. – Ari Pub Feb 02 '22 at 00:06
0

By the XAML you pass the type, not an object (instance of a type), so the switch statement doesn't find a match:

void Test ()
{
  var res1 = ObjectVersusTypeCheck(new Apple()); //"It's an apple"
  var res2 = ObjectVersusTypeCheck(typeof(Apple)); //"RuntimeType"     
} 

 class Apple { }

private string ObjectVersusTypeCheck(object o)
{
  switch (o)
  {
    case Apple _:
      return "It`s an apple";
    default:
      return o.GetType().Name;
  }
}

you could do something like:

void Test()
{
      var res1 = ObjectVersusTypeCheck(new Apple()); //"apple"
      var res2 = ObjectVersusTypeCheck(typeof(Apple)); //"apple"
      var res3 = ObjectVersusTypeCheck(new Cherry()); //"cherry"
      var res4 = ObjectVersusTypeCheck(typeof(Cherry));//"cherry"
}

private string ObjectVersusTypeCheck(object o)
{
  Type type = null;

  if (o is Type t)
  {
    type = t;
  }
  else
  {
    type = o.GetType();
  }

  if (type == typeof(Apple))
  {
    return "apple";
  }
  else if (type == typeof(Cherry))
  {
    return "cherry";
  }
  else
  {
    return "?";
  }

}

class Apple { }
class Cherry { }
dba
  • 1,159
  • 1
  • 14
  • 22
  • dba: Upon closer review of what I did in previous applications, I refactored my implementation for this application using a slightly different approach while retaining the same switch pattern matching by type implementation I had done previously while still being able to pass the type from XAML. Thank you for your inputs. – Ari Pub Feb 02 '22 at 00:07