I know that similar questions might have been asked before, but I am looking for an alternatvie solution to using IS, as I have read that IS is considered evil in the top-comment here.
So basically my Problem is that I have Abilities and Objects (Object A and Object B) that can use said abilities. However Object A lacks some data/information meaning it can only use some types of abilities (Ability A), while Object B has should be able to use all Abilities of type A and in addition some of type B.
For now I solved this the following way:
public abstract class BaseEffect
{
}
public abstract class GeneralEffect : BaseEffect
{
public abstract void Trigger(int damage, float factor);
}
public class GeneralEffectTest : GeneralEffect
{
public override void Trigger(int damage, float factor)
{
Console.WriteLine("Deal " damage * factor " damage");
}
}
public abstract class PlayerEffect : BaseEffect
{
public abstract void Trigger(int damage, string text, float factor);
}
public class PlayerEffectTest : PlayerEffect
{
public override void Trigger(int damage, string playerOnlyInfo, float factor)
{
Console.WriteLine("Deal " damage * factor " " text "-damage");
}
}
And basically a class that would be able to use both types of effects is the following:
public class TestClass
{
public BaseEffect effect;
public TestClass()
{
effect = new PlayerEffectTest();
}
public void Trigger()
{
if (effect is GeneralEffect)
{
((GeneralEffect)effect).Trigger(2, 0.5f);
}
else if (effect is PlayerEffect)
{
((PlayerEffect)effect).Trigger(2, "Fire", 0.2f);
}
}
}
A class that would be able to use just GeneralEffects would on the other hand just have a field defining a GeneralEffect.
I should maybe also note that for my actual use-case I would iterate through a List/Array of Effects.
Is there any way to avoid the "evil" IS here? How would you solve that issue?
CodePudding user response:
is
isn't evil. Everything depends on its usage. In fact, is
can be used since C# 7 in pattern matching which would allow you to write :
if (effect is GeneralEffect ge)
{
ge.Trigger(2, 0.5f);
}
else if (effect is PlayerEffect pe)
{
pe.Trigger(2, "Fire", 0.2f);
}
An even better option would be to use pattern matching with switch :
switch(effect)
{
case PlayerEffect pe:
pe.Trigger(2, "Fire", 0.2f);
break;
case GeneralEffect ge:
ge.Trigger(2, 0.5f);
break;
}
Pattern matching is far more powerful than this, as you can match on properties, not just types. It's also possible to add guard clauses:
switch(effect)
{
case PlayerEffect pe when player.Ammo>0:
pe.Trigger(2, "Fire", 0.2f);
break;
case PlayerEffect pe:
pe.Trigger(0, "Click", 0.0f);
break;
case GeneralEffect ge:
ge.Trigger(2, 0.5f);
break;
}