Ответ 1
Обычно я рекомендую базовый класс, но если это произойдет, вы можете сделать что-то вроде этого:
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 { }
Это делает каждый IAnimal
похожим на INoisyAnimal
, даже если он не является одним. Например:
IAnimal dog = new Dog();
IAnimal porcupine = new Porcupine();
Console.WriteLine(dog.MakeSound()); // bark
Console.WriteLine(porcupine.MakeSound()); // Unknown Noise
Однако это все еще не является реальной реализацией интерфейса. Обратите внимание, что, несмотря на появление
Console.WriteLine(porcupine is INoisyAnimal); // false
Другим вариантом может быть создание оболочки для расширения базового класса при необходимости новой функциональности:
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);
}
}
Затем вы можете создать INoisyAnimal
из любого IAnimal
, когда вам нужно:
INoisyAnimal dog = new Dog();
INoisyAnimal porcupine = new Porcupine().Noisy();
Console.WriteLine(dog.MakeSound()); // bark
Console.WriteLine(porcupine.MakeSound()); // Unknown Noise
Вы также можете сделать общий обертку (например, NoisyAnimal<T> where T : IAnimal, new
) и полностью избавиться от метода расширения. В зависимости от вашего фактического варианта использования это может быть предпочтительнее предыдущей опции.