简体   繁体   English

C# - 使用扩展方法提供默认接口实现

[英]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. 我刚学习C#扩展方法,并想知道我是否可以使用它来为接口提供默认实现。

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". 我希望豪猪和其他未明确实现MakeSound动物使用返回空字符串的默认扩展方法,但是狗和任何具有显式实现的动物都会返回自己的实现,例如“Bark”。

So my questions: 1. Is this doable? 所以我的问题:1。这可行吗? 2. If not, is there any other way to implement default behavior for an interface? 2.如果没有,是否有其他方法可以实现接口的默认行为?

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. 抽象类而不是接口不是一个选项,因为C#不支持多继承,而我的类继承了另一个类的行为。

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. 这使得每个IAnimal 看起来都像一个INoisyAnimal即使它不是真的。 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: 然后,您可以在需要时从任何IAnimal创建一个INoisyAnimal

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 (eg NoisyAnimal<T> where T : IAnimal, new ) and get rid of the extension method altogether. 你也可以使包装器通用(例如NoisyAnimal<T> where T : IAnimal, new )并完全摆脱扩展方法。 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 . 我不确切地知道你的实际情况是什么,或者你只是在试验,但是,如果只有一些动物吵闹,那么它可能是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. 然后,您将只调用MakeSound或实际上有噪声的对象。

How about something like this? 这样的事怎么样? It allows you to avoid having a base class, and you can do what you had in mind, right? 它允许你避免使用基类,你可以做你想到的,对吧?

public interface Animal
{
    // Fields
    string voice { get; }
}

public static class AnimalHelper
{
    // Called for any Animal
    public static string MakeSound(this Animal animal)
    {
        // Common code for all of them, value based on their voice
        return animal.voice;
    }
}

public class Dog : Animal
{
    public string voice { get { return "Woof!"; } }
}

public class Purcupine : Animal
{
    public string voice { get { return ""; } }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM