Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# - using extension methods to provide default interface implementation

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.

like image 268
tbkn23 Avatar asked Dec 06 '13 05:12

tbkn23


2 Answers

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.

like image 168
p.s.w.g Avatar answered Oct 16 '22 20:10

p.s.w.g


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.

like image 40
Fabio Milheiro Avatar answered Oct 16 '22 22:10

Fabio Milheiro