扩展方法如何工作,为什么不需要新的CLR?

Someone said recently that they thought extensions methods required a new CLR.

最近有人说他们认为扩展方法需要新的CLR。

Extension methods are a new feature in .NET 3.5 (C#3/VB9) that let you appear to "spot weld" new methods on to existing classes. If you think that the "string" object needs a new method, you can just add it and call it on instance variables.

扩展方法是.NET 3.5(C#3 / VB9)中的一项新功能,使您似乎可以将新方法“点焊”到现有类上。 如果您认为“字符串”对象需要新方法,则可以添加它并在实例变量上调用它。

Here's an example. Note that the IntHelper35 class below defines a new method for integers called DoubleThenAdd. Now, I can do things like 2.DoubleThenAdd(2). See how the method directly "hangs off" of the integer 2? It's the "this" keyword appearing before the first parameter that makes this magic work. But is it really magic? Did it require a change to the CLR, or just a really smart compiler?

这是一个例子。 请注意,下面的IntHelper35类为整数定义了一个称为DoubleThenAdd的新方法。 现在,我可以执行2.DoubleThenAdd(2)之类的操作。 看看方法如何直接“挂断”整数2? 使这个魔术起作用的第一个参数之前是“ this”关键字。 但这真的是魔术吗? 是否需要更改CLR或仅是真正的智能编译器?

Let's do some experimenting and see if we can figure it out for ourselves.

让我们做一些实验,看看是否可以自己解决。

using System;
namespace Foo
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(2.DoubleThenAdd(3));
Console.WriteLine(IntHelper20.DoubleThenAdd(2, 3));
Console.ReadLine();
}
}

public static class IntHelper20
{
public static int DoubleThenAdd(int myInt, int x)
{
return myInt + (2 * x);
}
}

public static class IntHelper35
{
public static int DoubleThenAdd(this int myInt, int x)
{
return myInt + (2 * x);
}
}
}

I've also added an IntHelper20 class with an identical method but WITHOUT the "this" keyboard. It's a standard static method, and I call it in the standard way. Now, let's compile it, then disassemble it with Reflector and take a look at the IL (Intermediate Language).

我还添加了具有相同方法但没有“ this”键盘的IntHelper20类。 这是一个标准的静态方法,我以标准方式进行调用。 现在,让我们对其进行编译,然后使用Reflector对其进行分解,然后看看IL(中间语言)。

.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 8
L_0000: nop
L_0001: ldc.i4.2
L_0002: ldc.i4.3
L_0003: call int32 ConsoleApplication8.IntHelper35::DoubleThenAdd(int32, int32)
L_0008: call void [mscorlib]System.Console::WriteLine(int32)
L_000d: nop
L_000e: ldc.i4.2
L_000f: ldc.i4.3
L_0010: call int32 ConsoleApplication8.IntHelper20::DoubleThenAdd(int32, int32)
L_0015: call void [mscorlib]System.Console::WriteLine(int32)
L_001a: nop
L_001b: call string [mscorlib]System.Console::ReadLine()
L_0020: pop
L_0021: ret
}

Interestingly, both method calls look the same. They look like static method calls with two integer parameters. From looking at this part of the IL, you can't actually tell which one is an extension method. We know the first one, IntHelper35, is, but from this snippet of IL, we can't tell.

有趣的是,两个方法调用看起来都一样。 它们看起来像带有两个整数参数的静态方法调用。 通过查看IL的这一部分,您实际上无法分辨出哪一种是扩展方法。 我们知道第一个是IntHelper35,但是从IL的这一片段中我们无法确定。

Can Reflector tell the difference if we ask it to decompile to C# or VB (rather than IL)?

如果我们要求将其反编译为C#或VB(而不是IL),则Reflector能否分辨出差异?

private static void Main(string[] args)
{
Console.WriteLine(2.DoubleThenAdd(3));
Console.WriteLine(IntHelper20.DoubleThenAdd(2, 3));
Console.ReadLine();
}

Interestingly, it knows the difference. How? Here's the decompilation of the IntHelper35 class itself:

有趣的是,它知道区别。 怎么样? 这是IntHelper35类本身的反编译:

.method public hidebysig static int32 DoubleThenAdd(int32 myInt, int32 x) cil managed
{
.custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor()
.maxstack 3
.locals init (
[0] int32 CS$1$0000)
L_0000: nop
L_0001: ldarg.0
L_0002: ldc.i4.2
L_0003: ldarg.1
L_0004: mul
L_0005: add
L_0006: stloc.0
L_0007: br.s L_0009
L_0009: ldloc.0
L_000a: ret
}

The only difference between the two methods is the CompilerServices.ExtensionAttribute. This attribute is added because of the "this" keyword, and it looks like it's what Reflector is using to correctly identify the extension method.

两种方法之间的唯一区别是CompilerServices.ExtensionAttribute。 添加此属性的原因是使用了“ this”关键字,它看起来像是Reflector用来正确标识扩展方法的对象。

Extension methods are a really nice syntactic sugar. They're not really added to the class, as we can see, but the compiler makes it feel like they are.

扩展方法是一个非常不错的语法糖。 正如我们所看到的,它们并没有真正添加到类中,但是编译器使它们看起来像它们一样。

关于面向对象的“ C”略有相关 (Slightly Related Aside about Object Oriented "C")

This reminded me of what we called "object oriented C" in college. I found a great example on Phil Bolthole's site.

这使我想起了大学时期所谓的“面向对象的C”。 我在Phil Bolthole的网站上找到了一个很好的例子

Basically you make a struct to represent your member variables, and then you create a number of methods where the first parameter is the struct. For example:

基本上,您会构造一个表示成员变量的结构,然后创建许多方法,其中第一个参数是该结构。 例如:

#include "FooOBJ.h" 
void diddle(){
FooOBJ fobj;

fobj=newFooOBJ(); /* create a new object of type "FooOBJ" */

/* Perform member functions on FooOBJ.
* If you try these functions on a different type of object,
* you will get a compile-time error
*/
setFooNumber(fobj, 1);
setFooString(fobj, "somestring");
dumpFooState(fobj);

deleteFooOBJ(fobj);
}

int main(){
diddle();
return 0;
}

In this C example, if you mentally move the first parameter to the left side and add a ".", like fobj.dumpFooState() it's almost like C++. Then, you ask yourself, "gosh, wouldn't it be nice if a compiler did this for me?"

在此C示例中,如果您在脑海中将第一个参数移到左侧并添加“。”,例如fobj.dumpFooState(),则几乎就像C ++。 然后,你问自己:“天哪,如果编译器为我这样做好吗?”

翻译自: https://www.hanselman.com/blog/how-do-extension-methods-work-and-why-was-a-new-clr-not-required

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值