'C# switch/case Pattern Matching When the Type is Passed as a CommandArgument From XAML
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 "??????";
}
}
Solution 1:[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;
}
Solution 2:[2]
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;
}
}
}
Solution 3:[3]
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 { }
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Amjad S. |
| Solution 3 | dba |
