答案 0 :(得分:11)
当您使用LINQ并希望将功能输出从一个函数链接或管道传输到另一个函数时,它们是有意义的。它提高了代码的可读性,使您能够更优雅地表达概念(无论价值多少)。
它们还允许您在不修改该类型的源的情况下为您喜欢的任何类型提供实例方法的外观,这通常可以帮助您在合理使用代码时的可读性和表现力
请注意,扩展方法调用如:
instance.SomeExtensionMethod()
编译为:
StaticExtensionMethodClass.SomeExtensionMethod(instance);
因此性能与任何其他静态方法调用相同。
答案 1 :(得分:2)
从我的回答here:
关于扩展方法的实际用法,您可以在不派生新类的情况下向类添加新方法。
看看下面的例子:
public class extended {
public int sum() {
return 7+3+2;
}
}
public static class extending {
public static float average(this extended extnd) {
return extnd.sum() / 3;
}
}
如您所见,班级Extending
正在向班级Extended
添加名为average的方法。要获得平均值,请调用average
方法,因为它属于extended
类:
extended ex = new extended();
Console.WriteLine(ex.average());
关于 Performance ,我认为你可以看到使用Extension方法的改进,因为它们永远不会动态调度,但这一切都取决于动态方法的实现方式。
答案 2 :(得分:1)
它的用途是扩展(添加)现有类的功能而不实际更改它们。
您可以在LINQ(System.Linq名称空间和其他名称)如何为所有集合添加许多功能中看到这一点。
答案 3 :(得分:1)
扩展方法的另一个有趣用途是,您希望将某些功能添加到一个名称空间下的类而不是另一个名称空间中。一个具体的例子是添加方法来简化单元测试 - 你不希望它们混乱你的生产组件,但是在编写单元测试时它们很棒。
答案 4 :(得分:1)
除了其他答案之外,扩展方法是向接口添加样板实现的好方法。例如,如果您希望所有列表都可以排序,请为IList<T>
添加扩展方法。
您可以(如前所述)使用扩展方法将方法添加到控件之外的类中;我想在Reverse()
上使用string
方法?加一个!
唯一的区别是扩展方法不使用虚拟,并且没有空检查。如果您愿意,可以使用此功能:
public static void ThrowIfNull<T>(this T obj, string name) where T : class
{
if(obj == null) throw new ArgumentNullException(name);
}
与常规实用程序方法不同,它们可以很容易地编写流畅的接口;这是他们存在的原因之一 - 即使用LINQ:
var foo = source.Where(predicate).OrderBy(selector);
比以下内容更具可读性:
var foo = Enumerable.OrderBy(Enumerable.Where(source,predicate),selector);
使用常规方法,要使用第一种方法,它必须是常规实例方法,这需要更改(例如)到IEnumerable<T>
- 不可取。
答案 5 :(得分:1)
扩展方法在使用函数编程的地方闪耀。
考虑您应用中已有的几乎所有模块级功能,以及将它们标记为扩展方法时的功能。它们成为“主动声音”而非“被动声音”的机会。主动语音意味着代码读取就好像实例提供了自己执行特定任务的方法,而不是让函数在其上被动地执行操作。
ExtendUnlimitedCredit(AddVIP(tblCustomer, "Bill Gates"))
VS
tblCustomer.AddVIP("Bill Gates").ExtendUnlimitedCredit()
扩展方法使这种转换变得简单。使用“主动语音”代码消除嵌套函数调用通常是一种改进。另外,由于我们的商店锁定了它的核心类,我们只能在扩展方法之前使用嵌套函数调用。
关于扩展方法的其他好处:
它们使您的功能(因为Intellisense)更容易被发现。如果您提供了描述函数用途和用途的内联标记,Intellisense甚至会提供一个有用的工具提示,描述方法及其用于发现它的开发人员(只需按下点)。未标记为扩展方法的函数不容易被发现,可能未被使用,因此,其他人可能会发明自己的所述函数的风格。
虽然您无法实际为接口实现方法,但扩展方法提供了另一种方法,可以提供您已经完成的外观。
答案 6 :(得分:0)
我不知道任何性能影响。当您无法访问源代码并因此无法直接将该方法添加到类中时,使用扩展方法最有意义,并且方法有意义作为函数实现。这是我在你上一个问题上做出的评论,其中另一个人在“字符串”类上提供了一个扩展方法的示例,该类根据字符串是否是有效的电子邮件返回了一个bool。这个IMO是不使用扩展方法的一个例子,因为该函数不是字符串类型的基础。但是,将Left(int)和Right(int)函数添加到'string'确实有意义。
答案 7 :(得分:0)
我使用它们来重用我的对象模型类。我有一堆类代表我在数据库中的对象。这些类仅在客户端使用,以显示对象,因此基本用法是访问属性。
public class Stock {
public Code { get; private set; }
public Name { get; private set; }
}
由于这种使用模式,我不希望在这些类中使用业务逻辑方法,因此我将每个业务逻辑都作为扩展方法。
public static class StockExtender {
public static List <Quote> GetQuotesByDate(this Stock s, DateTime date)
{...}
}
这样我就可以使用相同的类进行业务逻辑处理和用户界面显示,而不会使用不必要的代码重载客户端。
关于这个解决方案的一个有趣的事情是我的对象模型类是使用Mono.Cecil动态生成的,所以即使我想要添加业务逻辑方法也很困难。我有一个编译器,它读取XML定义文件并生成这些存根类,表示我在数据库中的一些对象。在这种情况下,唯一的方法是扩展它们。
答案 8 :(得分:0)
解决你的第二个问题:我的经验法则是,扩展方法应该是类型的“功能的自然扩展”,或者它应该是流畅界面的可维护和可读部分。
“功能的自然扩展”的一个示例是扩展数据读取器,以便在遇到DBNull时返回默认值。不太自然的是扩展数据读取器以返回由多个字段中的数据表示的实体的实例。在后一种情况下,你将错误的责任注入到可怜的对象中:)。
答案 9 :(得分:0)
/// <summary>
/// External Library Code
/// </summary>
namespace ExternalLibrary
{
public class Calculator
{
public int Number1 { get; set; }
public int Number2 { get; set; }
public int Addition()
{
return Number1 + Number2;
}
public int Subtraction()
{
return Number1 - Number2;
}
}
}
--------------------------------------------------------------------------------------
using ExternalLibrary;
using System;
namespace StackOverFlow
{
class Program
{
static void Main(string[] args)
{
Calculator calc = new Calculator()
{
Number1 = 5,
Number2 = 3
};
Console.WriteLine(calc.Addition());
Console.WriteLine(calc.Subtraction());
// Here we want multiplication also. but we don't have access Calculator
// class code, so we can't modify in that.
// In order to achieve this functionality we can use extension method.
Console.WriteLine(calc.Multiplication());
Console.ReadLine();
}
}
/// <summary>
/// Extension Method for multiplication
/// </summary>
public static class CalculatorExtension
{
public static int Multiplication(this Calculator calc)
{
return calc.Number1 * calc.Number2;
}
}
}