我遇到了以下代码:
public interface IFoo { }
通过扩展方法让IFoo
做一些事情:
public static FooExtensions
{
public static string Foo(this IFoo foo, string bar)
{
// Do work
return bar;
}
}
这是个好主意吗?为什么不使用带有虚拟Foo()
的抽象类呢? IFoo
可以有一些合同方法,但消费者也可以获得Foo()
扩展方法。
我的问题是:什么时候这样的好主意?
答案 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。
另一种方法如下:
您可能想要使用扩展方法的另一个原因是,您无法更改现有界面(例如,因为它是第三方)或您不想要(因为它会破坏现有代码) )。
答案 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();