C#中如何获取作为delegate传递的函数的原始名称
问题描述
你需要实现一个每5分钟触发一次的调度器,对所有每日仅需执行一次的任务做运行状态跟踪,每个作业的上次运行时间存储在静态字典LastRuntimePerJob中,仅当作业当日未运行过时才触发执行。
现有代码无法达到预期:使用nameof(jobToPerform)只会返回参数名"jobToPerform",无法获取传入委托绑定的实际函数名(例如示例中的Job1)。
原有问题代码如下:
using System; using System.Collections.Generic; namespace Euler.Business.Problem.Progress { public class Scheduler { public delegate void JobToPerform(); public void Job1() { // Perform some task } public static Dictionary<string, DateTime> LastRuntimePerJob { get; set; } = new Dictionary<string, DateTime>(); public void RunScheduler() { ExecuteWhenNecessary(Job1); } public void ExecuteWhenNecessary(JobToPerform jobToPerform) { var nameOfCurrentJob = nameof(jobToPerform); if (!LastRuntimePerJob.ContainsKey(nameOfCurrentJob) || LastRuntimePerJob[nameOfCurrentJob] < DateTime.Today) { jobToPerform(); if (LastRuntimePerJob.ContainsKey(nameOfCurrentJob)) { LastRuntimePerJob[nameOfCurrentJob] = DateTime.Now; } else { LastRuntimePerJob.Add(nameOfCurrentJob, DateTime.Now); } } } } }
实现方案
C#中所有委托都继承自System.Delegate类型,其内置的Method属性可以获取委托绑定的方法的MethodInfo元数据,直接读取Name属性即可拿到实际绑定的函数名。
核心修改点
将原来获取作业名的代码:
var nameOfCurrentJob = nameof(jobToPerform);
替换为:
var nameOfCurrentJob = jobToPerform.Method.Name;
注意事项
- 如果传入的委托是多播委托(绑定了多个方法),
Method属性只会返回多播列表中最后一个方法的信息,当前调度器场景下每个作业对应单个方法,不会出现这个问题 - 如果你的调度器需要加载不同类中定义的同名作业,直接用方法名会出现键冲突,可以用
$"{jobToPerform.Method.DeclaringType.FullName}.{jobToPerform.Method.Name}"作为唯一键,区分不同类下的同名方法 - 如果传入的是lambda表达式、匿名方法,获取到的会是编译器生成的带特殊字符的方法名,只要所有注册的作业都是显式定义的具名方法,该方案可以正常工作
修正后完整代码
using System; using System.Collections.Generic; namespace Euler.Business.Problem.Progress { public class Scheduler { public delegate void JobToPerform(); public void Job1() { // Perform some task } public static Dictionary<string, DateTime> LastRuntimePerJob { get; set; } = new Dictionary<string, DateTime>(); public void RunScheduler() { ExecuteWhenNecessary(Job1); } public void ExecuteWhenNecessary(JobToPerform jobToPerform) { // 从委托元数据中获取实际绑定的方法名 var nameOfCurrentJob = jobToPerform.Method.Name; if (!LastRuntimePerJob.ContainsKey(nameOfCurrentJob) || LastRuntimePerJob[nameOfCurrentJob] < DateTime.Today) { jobToPerform(); if (LastRuntimePerJob.ContainsKey(nameOfCurrentJob)) { LastRuntimePerJob[nameOfCurrentJob] = DateTime.Now; } else { LastRuntimePerJob.Add(nameOfCurrentJob, DateTime.Now); } } } } }
内容的提问来源于stack exchange,提问作者Vincent
相关产品推荐
相关产品推荐

