转:扩展方法(C# 编程指南)
扩展方法使你能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。扩展方法是一种特殊的静态方法,但可以像扩展类型上的实例方法一样进行调用。对于用 C# 和 Visual Basic 编写的客户端代码,调用扩展方法与调用在类型中实际定义的方法之间没有明显的差异。
最常见的扩展方法是 LINQ 标准查询运算符,它将查询功能添加到现有的 System.Collections.IEnumerable 和System.Collections.Generic.IEnumerable<T> 类型。若要使用标准查询运算符,请先使用 using System.Linq 指令将它们置于范围中。然后,任何实现了 IEnumerable<T> 的类型看起来都具有 GroupBy<TSource, TKey>、OrderBy<TSource, TKey>、Average 等实例方法。在IEnumerable<T> 类型的实例(如 List<T> 或 Array)后键入“dot”时,可以在 IntelliSense 语句完成中看到这些附加方法。
下面的示例演示如何对一个整数数组调用标准查询运算符 OrderBy 方法。括号里面的表达式是一个 lambda 表达式。很多标准查询运算符采用 lambda 表达式作为参数,但这不是扩展方法的必要条件。有关详细信息,请参阅 Lambda 表达式(C# 编程指南)。
class ExtensionMethods2
{ static void Main()
{
int[] ints = { 10, 45, 15, 39, 21, 26 };
var result = ints.OrderBy(g => g);
foreach (var i in result)
{
System.Console.Write(i + " ");
}
}
}
//Output: 10 15 21 26 39 45
扩展方法被定义为静态方法,但它们是通过实例方法语法进行调用的。它们的第一个参数指定该方法作用于哪个类型,并且该参数以 this 修饰符为前缀。仅当你使用 using 指令将命名空间显式导入到源代码中之后,扩展方法才位于范围中。
下面的示例演示为 System.String 类定义的一个扩展方法。请注意,它是在非嵌套的、非泛型静态类内部定义的:
namespace ExtensionMethods
{
public static class MyExtensions
{
public static int WordCount(this String str)
{
return str.Split(new char[] { ' ', '.', '?' },
StringSplitOptions.RemoveEmptyEntries).Length;
}
}
}
可使用此 using 指令将 WordCount 扩展方法置于范围中:
using ExtensionMethods;
而且,可以使用以下语法从应用程序中调用该扩展方法:
string s = "Hello Extension Methods";
int i = s.WordCount();
在代码中,可以使用实例方法语法调用该扩展方法。但是,编译器生成的中间语言 (IL) 会将代码转换为对静态方法的调用。因此,并未真正违反封装原则。实际上,扩展方法无法访问它们所扩展的类型中的私有变量。
有关详细信息,请参阅如何:实现和调用自定义扩展方法(C# 编程指南)。
通常,你更多时候是调用扩展方法而不是实现你自己的扩展方法。由于扩展方法是使用实例方法语法调用的,因此不需要任何特殊知识即可从客户端代码中使用它们。若要为特定类型启用扩展方法,只需为在其中定义这些方法的命名空间添加 using 指令。例如,若要使用标准查询运算符,请将此using 指令添加到代码中:
using System.Linq;
(你可能还必须添加对 System.Core.dll 的引用。)你将注意到,标准查询运算符现在作为可供大多数 IEnumerable<T> 类型使用的附加方法显示在 IntelliSense 中。
注意 |
---|
尽管标准查询运算符没有显示在 String 的 IntelliSense 中,但它们仍然可用。 |
可以使用扩展方法来扩展类或接口,但不能重写扩展方法。与接口或类方法具有相同名称和签名的扩展方法永远不会被调用。编译时,扩展方法的优先级总是比类型本身中定义的实例方法低。换句话说,如果某个类型具有一个名为 Process(int i) 的方法,而你有一个具有相同签名的扩展方法,则编译器总是绑定到该实例方法。当编译器遇到方法调用时,它首先在该类型的实例方法中寻找匹配的方法。如果未找到任何匹配方法,编译器将搜索为该类型定义的任何扩展方法,并且绑定到它找到的第一个扩展方法。下面的示例演示编译器如何确定要绑定到哪个扩展方法或实例方法。
下面的示例演示 C# 编译器在确定是将方法调用绑定到类型上的实例方法还是绑定到扩展方法时所遵循的规则。静态类 Extensions 包含为任何实现了IMyInterface 的类型定义的扩展方法。类 A、B 和 C 都实现了该接口。
MethodB 扩展方法永远不会被调用,因为它的名称和签名与这些类已经实现的方法完全匹配。
如果编译器找不到具有匹配签名的实例方法,它会绑定到匹配的扩展方法(如果存在这样的方法)。
// Define an interface named IMyInterface.
namespace DefineIMyInterface
{
using System; public interface IMyInterface
{
// Any class that implements IMyInterface must define a method
// that matches the following signature.
void MethodB();
}
} // Define extension methods for IMyInterface.
namespace Extensions
{
using System;
using DefineIMyInterface; // The following extension methods can be accessed by instances of any
// class that implements IMyInterface.
public static class Extension
{
public static void MethodA(this IMyInterface myInterface, int i)
{
Console.WriteLine
("Extension.MethodA(this IMyInterface myInterface, int i)");
} public static void MethodA(this IMyInterface myInterface, string s)
{
Console.WriteLine
("Extension.MethodA(this IMyInterface myInterface, string s)");
} // This method is never called in ExtensionMethodsDemo1, because each
// of the three classes A, B, and C implements a method named MethodB
// that has a matching signature.
public static void MethodB(this IMyInterface myInterface)
{
Console.WriteLine
("Extension.MethodB(this IMyInterface myInterface)");
}
}
} // Define three classes that implement IMyInterface, and then use them to test
// the extension methods.
namespace ExtensionMethodsDemo1
{
using System;
using Extensions;
using DefineIMyInterface; class A : IMyInterface
{
public void MethodB() { Console.WriteLine("A.MethodB()"); }
} class B : IMyInterface
{
public void MethodB() { Console.WriteLine("B.MethodB()"); }
public void MethodA(int i) { Console.WriteLine("B.MethodA(int i)"); }
} class C : IMyInterface
{
public void MethodB() { Console.WriteLine("C.MethodB()"); }
public void MethodA(object obj)
{
Console.WriteLine("C.MethodA(object obj)");
}
} class ExtMethodDemo
{
static void Main(string[] args)
{
// Declare an instance of class A, class B, and class C.
A a = new A();
B b = new B();
C c = new C(); // For a, b, and c, call the following methods:
// -- MethodA with an int argument
// -- MethodA with a string argument
// -- MethodB with no argument. // A contains no MethodA, so each call to MethodA resolves to
// the extension method that has a matching signature.
a.MethodA(1); // Extension.MethodA(object, int)
a.MethodA("hello"); // Extension.MethodA(object, string) // A has a method that matches the signature of the following call
// to MethodB.
a.MethodB(); // A.MethodB() // B has methods that match the signatures of the following
// method calls.
b.MethodA(1); // B.MethodA(int)
b.MethodB(); // B.MethodB() // B has no matching method for the following call, but
// class Extension does.
b.MethodA("hello"); // Extension.MethodA(object, string) // C contains an instance method that matches each of the following
// method calls.
c.MethodA(1); // C.MethodA(object)
c.MethodA("hello"); // C.MethodA(object)
c.MethodB(); // C.MethodB()
}
}
}
/* Output:
Extension.MethodA(this IMyInterface myInterface, int i)
Extension.MethodA(this IMyInterface myInterface, string s)
A.MethodB()
B.MethodA(int i)
B.MethodB()
Extension.MethodA(this IMyInterface myInterface, string s)
C.MethodA(object obj)
C.MethodA(object obj)
C.MethodB()
*/
通常,建议你只在不得已的情况下才实现扩展方法,并谨慎地实现。只要有可能,必须扩展现有类型的客户端代码都应该通过创建从现有类型派生的新类型来达到这一目的。有关详细信息,请参阅继承(C# 编程指南)。
在使用扩展方法来扩展你无法更改其源代码的类型时,你需要承受该类型实现中的更改会导致扩展方法失效的风险。
如果你确实为给定类型实现了扩展方法,请记住以下几点:
如果扩展方法与该类型中定义的方法具有相同的签名,则扩展方法永远不会被调用。
在命名空间级别将扩展方法置于范围中。例如,如果你在一个名为 Extensions 的命名空间中具有多个包含扩展方法的静态类,则这些扩展方法将全部由 using Extensions; 指令置于范围中。
针对已实现的类库,不应为了避免程序集的版本号递增而使用扩展方法。如果要向你拥有源代码的库中添加重要功能,应遵循适用于程序集版本控制的标准 .NET Framework 准则。有关详细信息,请参阅程序集版本控制。
转:扩展方法(C# 编程指南)的更多相关文章
- C# 扩展方法集
语法注意点 可以使用扩展方法来扩展类或接口. 不能重写扩展方法. 扩展方法只能在非嵌套.非泛型静态类内部定义. 扩展方法必须定义在静态类中. 扩展方法的第一个参数的类型用于指定被扩展的类型,它限制该扩 ...
- iOS ---Extension编程指南
当iOS 8.0和OS X v10.10发布后,一个全新的概念出现在我们眼前,那就是应用扩展.顾名思义,应用扩展允许开发者扩展应用的自定义功能和内容,能够让用户在使用其他app时使用该项功能.你可以开 ...
- App Extension编程指南(iOS8/OS X v10.10)中文版
http://www.cocoachina.com/ios/20141023/10027.html 当iOS 8.0和OS X v10.10发布后,一个全新的概念出现在我们眼前,那就是应用扩展.顾名思 ...
- c#编程指南(五) 扩展方法(Extension Method)
C# 3.0就引入的新特性,扩展方法可以很大的增加你代码的优美度,扩展方法提供你扩展.NET Framewoke类的扩展途径,书写和规则也简单的要命. 编写扩展方法有下面几个要求: 第一:扩展方法所在 ...
- ASP.NET MVC学前篇之扩展方法、链式编程
ASP.NET MVC学前篇之扩展方法.链式编程 前言 目的没有别的,就是介绍几点在ASP.NETMVC 用到C#语言特性,还有一些其他琐碎的知识点,强行的划分一个范围的话,只能说都跟MVC有关,有的 ...
- C#编译器优化那点事 c# 如果一个对象的值为null,那么它调用扩展方法时为甚么不报错 webAPI 控制器(Controller)太多怎么办? .NET MVC项目设置包含Areas中的页面为默认启动页 (五)Net Core使用静态文件 学习ASP.NET Core Razor 编程系列八——并发处理
C#编译器优化那点事 使用C#编写程序,给最终用户的程序,是需要使用release配置的,而release配置和debug配置,有一个关键区别,就是release的编译器优化默认是启用的.优化代码 ...
- C#编程(六十一)------------LINQ中的扩展方法
原文链接: http://blog.csdn.net/shanyongxu/article/details/47208401 LINQ中的扩展方法 LINQ中where扩展方法,要想使用,必须导入us ...
- C#编程(二十一)----------扩展方法
C#中的扩展方法 有许多扩展类的方式.如果有类的源代码,继承就是给类添加功能的好方法.但是如果没有源代码,怎么办?吃屎可以使用扩展方法,它允许改变一个类,但不需要该类的源代码.扩展方法是静态方法,它是 ...
- ASP.Net MVC开发基础学习笔记:二、HtmlHelper与扩展方法
一.一个功能强大的页面开发辅助类—HtmlHelper初步了解 1.1 有失必有得 在ASP.Net MVC中微软并没有提供类似服务器端控件那种开发方式,毕竟微软的MVC就是传统的请求处理响应的回归. ...
随机推荐
- 201521123091 《Java程序设计》第6周学习总结
Java 第六周总结 第六周的作业. 目录 1.本章学习总结 2.Java Q&A 3.码云上代码提交记录及PTA实验总结 1.本章学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以 ...
- 201521123082 《Java程序设计》第9周学习总结
201521123082 <Java程序设计>第9周学习总结 标签(空格分隔):java 1.本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 2. 书面作业 ...
- 201521123027 《JAVA程序设计》第五周学习总结
1.本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 1.2 可选:使用常规方法总结其他上课内容 Comparable接口与Comparator接口的区别: Markdown的其他用 ...
- 201521123011 《Java程序设计》第4周学习总结
1. 本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点. 1.2 使用常规方法总结其他上课内容. 答:1.super() 子类不能继承父类的构造方法,但可以通过super关键字去访问父类的构 ...
- ubuntu下chromium浏览器flash插件安装
ubuntu下chromium浏览器默认是不支持flash的,在新立德软件包中搜索flash得到的“Adobe Flash Player plugin installer”也没有什么卵用,因为装完以后 ...
- 201521123006 《java程序设计》 第14周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多数据库相关内容. 2. 书面作业 1. MySQL数据库基本操作 建立数据库,将自己的姓名.学号作为一条记录插入.(截图,需出现自 ...
- 201521123078 《java程序设计》第十周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 创建线程:定义Thread的子类可以实现Runable接口 线程的控制:线程暂停Thread.sleep() ...
- JAVA课程设计 学生成绩管理
学生成绩管理 可实现功能: 添加学生功能:姓名.学号.性别.出生年月日.(学号自动生成且唯一) 添加学生成绩功能:每个人都有数学.Java与体育四门课,可分课程输入成绩. 根据学生学号查找学生成绩功能 ...
- web前端 学习线路
1. 前言 大约在几个月之前,让我看完了<webkit技术内幕>这本书的时候,突然有了一个想法.想把整个web前端开发所需要的知识都之中在一个视图中,形成一个完整的web前端知识体系,目的 ...
- 王者荣耀是怎样炼成的(二)《王者荣耀》unity安装及使用的小白零基础入门
转载请注明出处:http://www.cnblogs.com/yuxiuyan/p/7535345.html 工欲善其事,必先利其器. 上回书说到,开发游戏用到unity和C#.本篇博客将从零开始做一 ...