Reputation: 572
There is new feature introduced in C# 7.1 called generic pattern matching. One of the examples I found looks like this:
void Attack(IWeapon weapon, IEnemy enemy)
{
switch (weapon)
{
case Sword sword:
// process sword attack
break;
case Bow bow:
// process bow attack
break;
}
}
But from my perspective it's a piece of incorrect design, which violates the second SOLID principle(open-close). I can't even think of the case where this might be needed, from my understanding if you came to situation when you need such switch, then you are doing something very wrong. From other hand, if this feature was added to language, there have to be a strong reason of doing so. So the question is - when would you need this assuming you're not creating poor architecture.
Upvotes: 2
Views: 671
Reputation: 35037
A. A switch
like this may be useful if you cannot (aka it's practically easier not to) modify classes you're working with, e.g. Exceptions.
B. pattern matching in switch
can be much more sophisticated than just discriminating on types.
Pattern matching from What's new in C# 7.0 shows this versatility.
public static int SumPositiveNumbers(IEnumerable<object> sequence)
{
int sum = 0;
foreach (var i in sequence)
{
switch (i)
{
case 0:
break;
case IEnumerable<int> childSequence:
{
foreach(var item in childSequence)
sum += (item > 0) ? item : 0;
break;
}
case int n when n > 0:
sum += n;
break;
case null:
throw new NullReferenceException("Null found in sequence");
default:
throw new InvalidOperationException("Unrecognized type");
}
}
return sum;
}
Upvotes: 1
Reputation: 1855
I agree that having switch statements all over your codebase is harder to maintain and don't scale well.
However, if Sword
and Bow
are not dependent on the class that contains the Attack
method, then you have no other option. And there are many examples for this like in serialization (as NibblyPig mentioned), but also when building a presentation layer, a ViewModel etc...
If Sword
and Bow
don't have the responsibility of processing an attack (which is where SRP is relevant) and you have a specific class for this case then you'll have to switch over different types. You don't want Sword
to have a method that deals with serialization and a method that deals with what color will the Sword
be in one specific menu etc...
Sure, it's helpful if you can avoid this by creating smart interfaces that abstract away the type and just allow you to access the information you need but sometimes you have no other options.
Also, sometimes language features can help to deal with legacy code and are not an indication of recommended good practices.
Upvotes: 0
Reputation: 52942
I agree that you can abuse it to violate open/closed, but you wouldn't use it in the way you have with your example (by taking in an interface).
Instead, consider a situation where you deserialize an object.
You might have a chat program and people can send messages, or images (pseudo code)
Object myObject = (object) _network.Read().Deserialize();
if (myObject is IImage) { }
else if (myObject is ITextMessage) { }
You could put this into a switch
instead. It's still open closed because you can write additional types that extend IImage
/ ITextMessage
.
You'd have to alter the fundamental functionality of the program to support things that aren't both of those. Or you could have an else { doSomeDefaultBehaviour(); }
Upvotes: 0