如何使用扩展方法使接口完整

How to use an extension method to make an interface complete

给定接口 IFoo

interface IFoo {
    void Do();
    void Stuff();
}

假设有(遗留)类 Foo1、Foo2、Foo3 都实现了 IFoo。

可以通过使用 IFoo 的一些方法来完成,或者在较新的 类 的情况下,只需使用 DoStuff()。实际上,人们可能会认为 DoStuff() 是 "forgotten" on IFoo.

还有较新的 类 FooX (FooY, ...) 实现 IFoo2,另外还有一个方法 DoStuff();

 interface IFoo2 : IFoo {
        void DoStuff();
   }

我需要接受 IFoo 对象,并且能够在其上 "Do Stuff"。

 //Let us assume foos = new IFoo[] {new Foo1(), new Foo2(), new Foo3(), new FooX()};

 void MyMethod(IFoo[] foos){
     foreach(foo in foos){
        //DoStuff is not defined in IFoo
        foo.DoStuff();
     }
 }

所以,我想为遗留的 IFoo 定义一个扩展方法 DoStuff() 类

public static DoStuff(this IFoo self){
    self.Do();
    self.Stuff();
}

不幸的是,这个扩展方法总是被调用,即使对于 FooX 也是如此。

我可以做类似

public static DoSomeStuff(this IFoo self){
    if(self is IFoo2) {
        (self as IFoo2).DoStuff()
    } else {
       self.Do();
       self.Stuff();
    }
}

void MyMethod(IFoo[] foos){
     foreach(foo in foos){            
        foo.DoSomeStuff();
     }
 }

但是,MyMethod 方法驻留在遗留项目中,目前尚不知道 IFoo2。不使用IFoo2是否可以找到解决方案?

只要您的变量具有 IFoo 类型,就会调用扩展方法 DoStuff。解决这个问题的通常方法正是您在上一段中提出的,或者确保在您希望调用更新的接口方法的地方使用 IFoo2 而不是 IFoo

您可以创建一个抽象子class,为目前尚未实现的 classes 实现 Do 和 Stuff 方法。

public abstract class abstractFoo : IFoo 
{
    public virtual void Do() {}
    public virtual void Stuff(){}
}

如果可以继承这个抽象class

public class Foo: IFoo 
{
    // interface implementation required
}

becomes:

public class Foo: abstractFoo
{
    // interface implementation NOT required
}

你不应该像那样扩展 IFoo 接口。休息了 Interface Segregation principle.

如果这些对象在您的代码中表示完全相同的实体,则不应为它们使用不同的接口。 如果您想要扩展实现接口 IFoo 的 类 的功能,您可以创建扩展方法,但不要创建表示相同合同的第二个接口。但是,如果您想更改 IFoo 合同 - 重构遗留对象(添加缺少的实现)。

恕我直言 FooX 不应该开始实施 IFoo 并且应该对您当前的架构进行一些重新考虑。

就是说,虽然不知道您要克服的限制到底是什么,但您可以通过包装器发送 IFooX 吗?类似于以下内容:

public class FooXWrapper<T>: IFoo where T: FooX
{
    readonly T foo;
    bool doCalled;

    public FooWrapper(T foo)
    {
        this.foo = foo;
    }

    public void Do()
    {
        doCalled = true;
    }

    public void Stuff()
    {
         if (!doCalled)
             throw new InvalidOperationException("Must call Do");

         foo.DoStuff();
    }
}

这是一个丑陋的 hack,但考虑到情况...