扩大要领使你能够向现有范例“增加”要领,而无需建立新的派生范例、从新编译或以其他体式格局修正原始范例。 扩大要领是一种特别的静态要领,但能够像扩大范例上的实例要领一样举行挪用。 关于用 C# 和 Visual Basic 编写的客户端代码,挪用扩大要领与挪用在范例中现实定义的要领之间没有显著的差别。
最常见的扩大要领是 LINQ 规范查询运算符,它将查询功用增加到现有的 System.Collections.IEnumerable 和 System.Collections.Generic.IEnumerable<T> 范例。 若要运用规范查询运算符,请先运用 using System.Linq 指令将它们置于局限中。 然后,任何完成了 IEnumerable<T> 的范例看起来都具有 GroupBy、OrderBy、Average 等实例要领。 在 IEnumerable<T>范例的实例(如 List<T> 或 Array)后键入“dot”时,能够在 IntelliSense 语句完成中看到这些附加要领。
下面的示例演示怎样对一个整数数组挪用规范查询运算符 OrderBy 要领。 括号内里的表达式是一个 lambda 表达式。 许多规范查询运算符采纳 lambda 表达式作为参数,但这不是扩大要领的必要条件。 有关详细信息,请参阅 Lambda 表达式(C# 编程指南)。
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 类定义的一个扩大要领。 请注意,它是在非嵌套的、非泛型静态类内部定义的:
C#
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 扩大要领永久不会被挪用,由于它的称号和署名与这些类已完成的要领完整婚配。
假如编译器找不到具有婚配署名的实例要领,它会绑定到婚配的扩大要领(假如存在如许的要领)。
C#
// 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# 参数带this是什么意义(扩大要领)的内容,更多相关内容请关注ki4网(www.ki4.cn)!