Atr*_*ace 4 c# methods types function
委托如何存储对函数的引用?源代码似乎将其称为Object,并且它调用方法的方式似乎从源代码中编辑.任何人都可以解释C#如何处理这个问题?
我似乎经常与C#对其程序员施加的抽象作斗争.一直困扰着我的是函数/方法的混淆.据我了解,所有方法实际上都是分配给类属性的匿名方法.这就是为什么没有函数以数据类型为前缀的原因.例如...
void foo() { ... }
Run Code Online (Sandbox Code Playgroud)
...将用Javascript编写为......
Function foo = function():void { ... };
Run Code Online (Sandbox Code Playgroud)
根据我的经验,匿名函数通常是不好的形式,但在这里它充满了整个语言标准.因为您无法使用其数据类型定义函数(并且显然编译器假定了隐含/处理),如果从未声明类型,如何存储对方法的引用?
我正在努力避免Delegates及其变种(Action&Func),因为...
查看源代码Delegate.cs,它似乎简单地引用了函数的引用Object(参见第23-25行).
如果这些确实是对象,我们如何称呼它们?根据delegate.cs路径,它在以下路径上死路一条:
Delegate.cs:DynamicInvoke()> DynamicInvokeImpl()> methodinfo.cs:UnsafeInvoke()> UnsafeInvokeInternal()> RuntimeMethodHandle.InvokeMethod()>runtimehandles.cs:InvokeMethod()
internal extern static object InvokeMethod(object target, object[] arguments, Signature sig, bool constructor);
Run Code Online (Sandbox Code Playgroud)
这实际上并没有解释如果它确实是一个对象,它是如何调用的.感觉好像这根本不是代码,并且调用的实际代码已经从源代码库中编辑.
非常感谢您的帮助.
@Amy:我在声明之后立即举了一个例子来解释我的意思.如果函数以数据类型为前缀,则可以编写一个真正的匿名函数,并将其作为属性存储到Object,例如:
private Dictionary<string, Function> ops = new Dictionary<string, Function> {
{"foo", int (int a, int b) { return a + b } }
};
Run Code Online (Sandbox Code Playgroud)
既然这样,C#不允许你写真正的匿名功能,和墙壁该功能关闭落后Delegates和Lambda expressions.
@ 500内部服务器错误:我已经解释了我想要做的事情.我甚至加粗了.你认为这里有任何别有用心的动机; 我只是想了解C#如何存储对方法的引用.我甚至提供了源代码的链接,以便其他人可以自己阅读代码并帮助回答问题.
@Dialecticus:显然,如果我已经在谷歌找到了典型的答案,那么找到我正在寻找答案的唯一其他地方就是在这里.我意识到这超出了大多数C#开发人员的知识,这就是我提供源代码链接的原因.如果您不知道答案,则无需回复.
虽然我不完全理解你对"真正的匿名函数","没有数据类型前缀"等的见解,但我可以解释一下用C#调用方法编写的应用程序.
首先,C#中没有这样的"功能".C#中的每个可执行实体实际上都是一种方法,也就是说,它属于一个class.即使您定义lambdas或匿名函数,如下所示:
collection.Where(item => item > 0);
Run Code Online (Sandbox Code Playgroud)
C#编译器在后台创建一个编译器生成的类,并将lambda主体return item > 0放入编译器生成的方法中.
所以假设你有这个代码:
class Example
{
public static void StaticMethod() { }
public void InstanceMethod() { }
public Action Property { get; } = () => { };
}
static class Program
{
static void Main()
{
Example.StaticMethod();
var ex = new Example();
ex.InstanceMethod();
ex.Property();
}
}
Run Code Online (Sandbox Code Playgroud)
C#编译器将创建一个IL代码.IL代码不能立即执行,需要在虚拟机中运行.
IL代码将包含一个Example具有两个方法的类(实际上,四个 - 一个默认构造函数和属性getter方法将自动生成)和一个编译器生成的类,其中包含一个方法,该方法的主体是lambda表达式的主体.
IL代码Main将如下所示(简化):
call void Example::StaticMethod()
newobj instance void Example::.ctor()
callvirt instance void Example::InstanceMethod()
callvirt instance class [mscorlib]System.Action Example::get_Prop()
callvirt instance void [mscorlib]System.Action::Invoke()
Run Code Online (Sandbox Code Playgroud)
请注意那些call和callvirt说明:这些是方法调用.
要实际执行被调用的方法,需要将其IL代码编译为机器代码(CPU指令).这发生在名为.NET Runtime的虚拟机中.其中有几个像.NET Framework,.NET Core,Mono等.
.NET运行时包含JIT(即时)编译器.它在程序执行期间将IL代码转换为实际可执行代码.
当.NET运行时首次遇到IL代码" StaticMethod从类调用方法Example"时,它首先查看已编译方法的内部缓存.当没有匹配(这意味着这是该方法的第一次调用)时,运行时要求JIT编译器使用IL代码创建这样一个已编译并准备运行的方法.IL代码转换为一系列CPU操作并存储在进程的内存中.指向该编译代码的指针存储在缓存中以供将来重用.
这一切都将在IL call或callvirtIL指令背后发生(再次,简化).
一旦发生这种情况,Runtime就可以执行该方法了.CPU将编译代码的第一个操作地址作为下一个要执行的操作,并继续执行直到代码返回.然后,Runtime再次接管并继续下一个IL指令.
DynamicInvoke委托的方法做同样的事情:它指示运行时调用一个方法(在一些额外的参数检查之后).你提到的"死胡同" RuntimeMethodHandle.InvokeMethod是对Runtime的内在调用.该方法的参数是:
object target- 委托调用实例方法(this参数)的对象.object[] arguments - 传递给方法的参数.Signature sig- 要调用的实际方法Signature是一个内部类,它提供托管IL代码和本机可执行代码之间的连接.bool constructor- true如果这是一个构造函数调用.因此,在总结,方法并不表示为objectS IN C#(当你当然可以有一个delegate实例是一个object,但它并不代表可执行方法,而提供了一个可调用参照它).
方法由Runtime调用,JIT编译器使方法可执行.
您无法在C#中的类之外定义全局"函数".你可以得到一个直接的本地指针编译(即时编译)方法的代码,甚至可能通过直接操作自己的过程的记忆手动调用它.但为什么?