通过扩展方法为IFoo.Foo()提供实现

时间:2014-03-13 11:56:18

标签: c# class interface extension-methods

我遇到了以下代码:

public interface IFoo { }

通过扩展方法让IFoo做一些事情:

public static FooExtensions 
{
    public static string Foo(this IFoo foo, string bar)
    {
        // Do work
        return bar;
    }
}

这是个好主意吗?为什么不使用带有虚拟Foo()的抽象类呢? IFoo可以有一些合同方法,但消费者也可以获得Foo()扩展方法。

我的问题是:什么时候这样的好主意?

6 个答案:

答案 0 :(得分:3)

扩展方法不会“使”IFoo做任何事情。扩展方法只是让你扩展一个已关闭的类型......它通常最好与你无法修改的代码一起使用,例如框架类型或第三方类型。

另一种可能性是,如果您的接口的所有实现都有很多逻辑完全相同,并且您希望接口的使用者能够访问该功能而无需使用基本类型。想想LINQ - 它是通过扩展方法实现的,只需实现IEnumerable就可以获得它的所有好处。

在这种情况下,除了不必要的间接层之外,你没有获得任何东西。如果IFoo能够执行Foo,请在界面中添加Foo

答案 1 :(得分:2)

当您不想要或无法改变您正在扩展的课程的实施时,扩展方法是个好主意。 IFoo可以在第三方库中声明。或者可能有很多代码依赖于它,因此很难将其重新映射到抽象类(可能某些反射依赖于接口)。

一般来说,从使用的角度来看,当它看起来比老式的静态方法更具可读性时,你应该使用扩展方法,无论如何你应该使用静态方法而不是新的类成员。在考虑扩展方法 vs 成员时,请考虑辅助类中的静态方法 vs < strong>会员和如果您选择静态,请考虑将其实施为扩展是否更好。

但是我经常看到使用扩展方法,它实际上并不是必需的,并且通常会使代码的可读性降低。所以我不建议在如何避免使用它们时使用它们

答案 2 :(得分:1)

  

什么时候这样的好主意?

当您需要使用新技巧教授现有成员实现此界面时,例如来自System.Core程序集的

// System.Linq.Enumerable
public static TSource First<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    if (predicate == null)
    {
        throw Error.ArgumentNull("predicate");
    }
    foreach (TSource current in source)
    {
        if (predicate(current))
        {
            return current;
        }
    }
    throw Error.NoMatch();
}

答案 3 :(得分:1)

您可能希望这样做的原因是您希望接口提供方法,并且始终可以使用接口中的其他方法和属性来完成该方法的实现。

接口(与抽象基类不同)使您无法提供&#34;默认&#34;方法的实现。通过使用扩展方法,您可以提供这样的方法,而接口的所有实现者都不必提供相同的重复实现代码。

然而,这种方法的一个主要缺点是扩展方法中的方法是有效密封的 - 您无法以不同方式实现它。有时这是好的,有时候不是 - YMMV。

另一种方法如下:

  1. 像往常一样指定您的界面,但将相关方法添加到其中。
  2. 提供一个抽象基类,它提供相关方法的默认代码。
  3. 当您想要提供此接口的实现时,从抽象基类派生。
  4. 您可能想要使用扩展方法的另一个原因是,您无法更改现有界面(例如,因为它是第三方)或您不想要(因为它会破坏现有代码) )。

答案 4 :(得分:1)

扩展方法只是语法糖,可让您将fun(t, x)更改为t.fun(x)。它们对发现(intellisense)非常有用,或者当你想要编写流畅的函数管道时,这些函数遵循更直观的方式。从左到右的风格,而不是从右到左。例如f(x).g(y).h(z)h(g(f(x),y),z)

除了杂乱的智能感知之外,使用它们并没有任何不利之处。

答案 5 :(得分:1)

当您想要将此实现提供给实现该接口的任何对象时,这是一个好主意,无论该实现是什么实现。

抽象类仅将实现提供给其派生类。

如果该接口是您的,或者,您有一个实现该接口的基本抽象类,并且可以安全地假设没有从该类派生的实现将在您的代码 - 在该抽象类中实现该功能是个好主意(但是,您必须转换为该抽象类,以使用该方法,这使得接口在某种程度上是多余的)。

但是,如果要为实现该接口的所有类型提供(该方法)的实现,无论其实际实现如何 - 扩展方法都是更好的主意。 此外,类只能从单个类派生 - 这意味着通过从该抽象类派生,您无法从任何其他类派生。因此,如果您有多个实现该接口的继承链,那么为所有这些(直接)提供该方法而不重复代码的唯一解决方案是通过扩展(尽管还有其他解决方案来提供功能) ,但它不会直接:objWhichImplIFoo.Foo())。

顺便说一下,还有另一个需要扩展的理由:如果你希望它可以从null调用。如果对象为null,则声明的方法将始终抛出NullReferenceException。因为扩展实际上是静态方法 - 它们可以在nulls上调用:

IFoo foo = null;
var something = foo.GetSomethingOrDefault();