1、写在前面

今天群里一个小伙伴问了这样一个问题,扩展方法与实例方法的执行顺序是什么样子的,谁先谁后(这个问题会在文章结尾回答)。所以写了这边文章,力图从原理角度解释扩展方法及其使用。

以下为主要内容:

  • 什么是扩展方法
  • 扩展方法原理及自定义扩展方法
  • 扩展方法的使用及其注意事项
 
2、什么是扩展方法

一般而言,扩展方法为现有类型添加新的方法(从面向对象的角度来说,是为现有对象添加新的行为)而无需修改原有类型,这是一种无侵入而且非常安全的方式。扩展方法是静态的,它的使用和其他实例方法几乎没有什么区别。常见的扩展方法有Linq扩展、有IEnumerable扩展等。
     先让我们来感受一下.NET中自带的扩展方法,其中OrderBy和Aggregate都是系统自带的扩展方法

using System;
using System.Collections.Generic;
using System.Linq; namespace Test
{
class Program
{
static void Main(string[] args)
{
List<int> lst = new List<int> { , , , }; string result = lst.OrderBy(p => p).Aggregate(string.Empty, (next, p) => next += p + ","); Console.WriteLine(result); Console.ReadLine();
}
}
}

输出结果:

是不是感觉扩展方法很优美,使用起来和实例方法几乎没有区别。不得不说.NET在这方面做得很精致,很让人钦佩,那么接下来我们来看看扩展方法的原理

3、扩展方法原理及自定义扩展方法

首先我们,先看看如何自定义扩展方法

using System;
using TestExtension; namespace Test
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("".ToInt32()); Console.ReadLine();
}
}
} namespace TestExtension
{
public static class StringExtension
{
public static int ToInt32(this string str)
{
if (int.TryParse(str, out int result))
{
return result;
} throw new ArgumentException("无法转换为Int32类型");
}
}
}

通过以上实例,我们可以知道自定义扩展方法需要做到:

  • 必须是静态类,扩展方法也为静态方法
  • 此方法的第一个参数指定方法所操作的类型;此参数前面必须加上 this 修饰符
  • 在调用代码中,如何不再同一个命名空间,需要添加 using 指令,导入需要调用的扩展方法所在的命名空间
  • 需要注意的是,第一个this标记的参数并非是实参,而是标识该扩展所指定的类型,调用的时候只需要提供this后的形参即可

接下来我们来探究一下扩展方法反编译后的效果:

这是StringExtension编译后的代码,可以看到扩展方法在编译后被标记了ExtensionAttribute这个特性,也就是说扩展方法在编译期就已经被绑定成扩展方法了

.class public auto ansi abstract sealed beforefieldinit TestExtension.StringExtension
extends [System.Runtime]System.Object
{
.custom instance void

[System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute

::.ctor() = (

    )
// Methods
.method public hidebysig static
int32 ToInt32 (
string str
) cil managed
{
.custom instance void

[System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute

::.ctor() = (

        )
// Method begins at RVA 0x2050
// Code size 31 (0x1f)
.maxstack
.locals init (
[] int32,
[] bool,
[] int32
) IL_0000: nop
IL_0001: ldarg.
IL_0002: ldloca.s
IL_0004: call bool [System.Runtime]System.Int32::TryParse(string, int32&)
IL_0009: stloc.
IL_000a: ldloc.
IL_000b: brfalse.s IL_0012 IL_000d: nop
IL_000e: ldloc.
IL_000f: stloc.
IL_0010: br.s IL_001d IL_0012: ldstr "无法转换为Int32类型"
IL_0017: newobj instance void [System.Runtime]System.ArgumentException::.ctor(string)
IL_001c: throw IL_001d: ldloc.
IL_001e: ret
} // end of method StringExtension::ToInt32 } // end of class TestExtension.StringExtension

我们看一下调用后的效果,和直接调用静态方法一样TestExtension.StringExtension::ToInt32(string) ,至此,我们已经知道了扩展方法的使用了,编译器绑定,底层调用和静态调用一直,这也解释了一个问题,就是当类型为空的时候,为什么调用扩展方法了

.namespace Test
{
.class private auto ansi beforefieldinit Test.Program
extends [System.Runtime]System.Object
{
// Methods
.method private hidebysig static
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x207b
// Code size 24 (0x18)
.maxstack
.entrypoint IL_0000: nop
IL_0001: ldstr ""
IL_0006: call int32 TestExtension.StringExtension::ToInt32(string)
IL_000b: call void [System.Console]System.Console::WriteLine(int32)
IL_0010: nop
IL_0011: call string [System.Console]System.Console::ReadLine()
IL_0016: pop
IL_0017: ret
} // end of method Program::Main .method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2094
// Code size 8 (0x8)
.maxstack IL_0000: ldarg.
IL_0001: call instance void [System.Runtime]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method Program::.ctor } // end of class Test.Program }
4、扩展方法的使用及其注意事项

扩展方法虽然很好用,但是如果我们扩展的对象发生了版本迭代,则会增加扩展方法失效的风险。

一下是在使用扩展方法时需要注意的地方

  • 扩展方法与该类型中定义的方法具有相同的签名,编译器总是绑定到该实例方法,也就是扩展方法永远不会被调用,这也就回答了题目刚开始所说的问题。同时这个地方应该是考虑到了程序安全的问题,不然很容易出现代码注入问题。
  • 当出现命名空间不同时,则需要使用using导入命名空间
  • 同时扩展方法可以被修饰为internal,public,但需要类和扩展方法保持同样的修饰标识

【细语】C#之扩展方法原理及其使用的更多相关文章

  1. C#的扩展方法解析

    在使用面向对象的语言进行项目开发的过程中,较多的会使用到“继承”的特性,但是并非所有的场景都适合使用“继承”特性,在设计模式的一些基本原则中也有较多的提到. 继承的有关特性的使用所带来的问题:对象的继 ...

  2. C# 知识回顾 - 扩展方法解析

    在使用面向对象的语言进行项目开发的过程中,较多的会使用到“继承”的特性,但是并非所有的场景都适合使用“继承”特性,在设计模式的一些基本原则中也有较多的提到. 继承的有关特性的使用所带来的问题:对象的继 ...

  3. .NET Core中间件的注册和管道的构建(3) ---- 使用Map/MapWhen扩展方法

    .NET Core中间件的注册和管道的构建(3) ---- 使用Map/MapWhen扩展方法 0x00 为什么需要Map(MapWhen)扩展 如果业务逻辑比较简单的话,一条主管道就够了,确实用不到 ...

  4. .NET Core中间件的注册和管道的构建(2)---- 用UseMiddleware扩展方法注册中间件类

    .NET Core中间件的注册和管道的构建(2)---- 用UseMiddleware扩展方法注册中间件类 0x00 为什么要引入扩展方法 有的中间件功能比较简单,有的则比较复杂,并且依赖其它组件.除 ...

  5. 为IEnumerable<T>添加RemoveAll<IEnumerable<T>>扩展方法--高性能篇

    最近写代码,遇到一个问题,微软基于List<T>自带的方法是public bool Remove(T item);,可是有时候我们可能会用到诸如RemoveAll<IEnumerab ...

  6. [C#详解] (1) 自动属性、初始化器、扩展方法

    文章来源:Slark.NET-博客园 http://www.cnblogs.com/slark/p/CSharp-focus-1.html 代码下载:点我下载 目录 前言 属性与自动属性 属性 自动属 ...

  7. C#扫盲之:带你掌握C#的扩展方法、以及探讨扩展方法的本质、注意事项

    1.为什么需要扩展方法 .NET3.5给我们提供了扩展方法的概念,它的功能是在不修改要添加类型的原有结构时,允许你为类或结构添加新方法. 思考:那么究竟为什么需要扩展方法呢,为什么不直接修改原有类型呢 ...

  8. 使用Map/MapWhen扩展方法

    使用Map/MapWhen扩展方法 .NET Core中间件的注册和管道的构建(3) ---- 使用Map/MapWhen扩展方法 0x00 为什么需要Map(MapWhen)扩展 如果业务逻辑比较简 ...

  9. 在.NET下学习Extjs(第四个案例 Extjs扩展的原理)

    1.构建如下代码 <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head& ...

随机推荐

  1. Hibernte

    什么是CRM?(了解) CRM(customer relationship management)即客户关系管理,是指企业用CRM技术来管理与客户之间的关系.在不同场合下,CRM可能是一个管理学术语, ...

  2. Matlab调用遗传工具箱复现论文模型求解部分

    原文转载自:https://blog.csdn.net/robert_chen1988/article/details/52431594 论文来源: https://www.sciencedirect ...

  3. kettle web化

    kettle web化 通过Java API调用kettle核心代码,并基于Spring Boot提供简易的Web管理界面. 背景 在工作中,通过kettle这款ETL产品进行数据处理时,是通过kit ...

  4. 自然语言处理(四)统计机器翻译SMT

    1.统计机器翻译三要素 1.翻译模型 2.语言模型 3.排序模型 2.翻译流程 1.双语数据预处理 2.词对齐 3.构造短语翻译表 4.对短语翻译表进行概率估计 5.解码,beam search 6. ...

  5. node04

    1.模板引擎 用于渲染页面 介绍jade或ejs jade:侵入式,与原生html/css不共存,使用缩进代表层级 模板后缀.jade ejs:则非侵入式的 2.jade 1)简单使用: //代码 c ...

  6. Linux系统下如何运行.sh文件

    在Linux系统下运行.sh文件有两种方法,比如我在root目录下有个datelog.sh文件 第一种(这种办法需要用chmod使得文件具备执行条件(x): chmod u+x datelog.sh) ...

  7. 使用Nginx做图片服务器时候,配置之后图片访问一直是 404问题解决

    我的错误配置是: 服务器文件根地址: 想通过浏览器输入这个地址访问到图片: 但是会发现文件找不到会一直404,原因是根路径配置错误,来看下root路径原理: root 配置的意思是,会在root配置的 ...

  8. [Java]LeetCode117. 填充同一层的兄弟节点 II | Populating Next Right Pointers in Each Node II

    Given a binary tree struct TreeLinkNode { TreeLinkNode *left; TreeLinkNode *right; TreeLinkNode *nex ...

  9. [Swift]LeetCode689. 三个无重叠子数组的最大和 | Maximum Sum of 3 Non-Overlapping Subarrays

    In a given array nums of positive integers, find three non-overlapping subarrays with maximum sum. E ...

  10. 构建multipart/form-data实现文件上传

    构建multipart/form-data实现文件上传 通常文件上传都是通过form表单中的file控件,并将form中的content-type设置为multipart/form-data.现在我们 ...