I'm just learning about C# extension methods, and was wondering if I can use it to provide a default implementation for an interface.
Say:
public interface Animal {
string MakeSound();
}
public static string MakeSound(this Animal) {
return "";
}
Then
public class Dog : Animal {
string MakeSound() {
return "Bark";
}
}
public class Porcupine : Animal {
}
And last:
Animal dog = new Dog();
Animal porcupine = new Porcupine();
Print(dog.MakeSound());
Print(porcupine.MakeSound());
I'd like the porcupine and any other animals that have not explicitly implemented MakeSound
to use the default extension method that returns an empty string, but dog and any animal that does have an explicit implementation return its own implementation such as "Bark".
So my questions: 1. Is this doable? 2. If not, is there any other way to implement default behavior for an interface?
Abstract class instead of interface is not an option because C# doesn't support multiple inheritance and my classes are inheriting behavior of another class.
I'd generally recommend a base class, however, if that's out, you can do something like this:
public interface IAnimal { }
public interface INoisyAnimal : IAnimal {
string MakeSound();
}
public static class AnimalExtensions {
public static string MakeSound(this IAnimal someAnimal) {
if (someAnimal is INoisyAnimal) {
return (someAnimal as INoisyAnimal).MakeSound();
}
else {
return "Unknown Noise";
}
}
}
public class Dog : INoisyAnimal {
public string MakeSound() {
return "Bark";
}
}
public class Porcupine : IAnimal { }
This makes every IAnimal
look like a INoisyAnimal
even if it isn't really one. For example:
IAnimal dog = new Dog();
IAnimal porcupine = new Porcupine();
Console.WriteLine(dog.MakeSound()); // bark
Console.WriteLine(porcupine.MakeSound()); // Unknown Noise
However, this still isn't an actual implementation of the interface. Notice that despite appearances
Console.WriteLine(porcupine is INoisyAnimal); // false
Another option might be to create a wrapper to extend your base class when new functionality is needed:
public class NoisyAnimalWrapper : INoisyAnimal {
private readonly IAnimal animal;
public NoisyAnimalWrapper(IAnimal animal) {
this.animal = animal;
}
public string MakeSound() {
return "Unknown Noise";
}
}
public static class AnimalExtensions {
public static INoisyAnimal Noisy(this IAnimal someAnimal) {
return someAnimal as INoisyAnimal ??
new NoisyAnimalWrapper(someAnimal);
}
}
Then you can create a INoisyAnimal
from any IAnimal
whenever you need to:
INoisyAnimal dog = new Dog();
INoisyAnimal porcupine = new Porcupine().Noisy();
Console.WriteLine(dog.MakeSound()); // bark
Console.WriteLine(porcupine.MakeSound()); // Unknown Noise
You could also make the wrapper generic (e.g. NoisyAnimal<T> where T : IAnimal, new
) and get rid of the extension method altogether. Depending on your actual use case, this may be preferable to the previous option.
I don't know exactly what is your real case or if you are just experimenting but, if only some animals are noisy, then it might a good case for Interface segregation
.
For example:
public class Dog : IAnimal, INoisy
{
public string MakeSound()
{
return "Bark";
}
}
public class Porcupine : IAnimal
{
}
Then, you will only call MakeSound
or objects that are actually noisy.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With