【问题标题】:How to call delegate from string in C#?如何在 C# 中从字符串调用委托?
【发布时间】:2011-05-15 19:05:49
【问题描述】:

是否可以通过变量名(作为字符串)调用存储在变量中的委托?我想我必须使用反射机制,但我没有得到任何地方

示例代码:

class Demo {
  public delegate int DemoDelegate();

  private static int One() {
    return 1;
  }
  private static void CallDelegate(string name) {
    // somehow get the value of the variable with the name
    // stored in "name" and call the delegate using reflection
  }
  private static void CallDelegate(string name, DemoDelegate d) {
    d();
  }
  static void main(string[] args) {
    DemoDelegate one = Demo.One;
    CallDelegate(one);
    // this works, but i want to avoid writing the name of the variable/delegate twice:
    CallDelegate("one", one);
  }

}

这甚至可能吗?如果是这样怎么办?如果没有,那我只能用第二种形式了,倒霉

【问题讨论】:

  • 在第二个重载中使用DemoDelegate d 完全违背了string name 的目的,不是吗?
  • @boltclock: 是的,这就是我想避免它的原因(在我的真实代码中,我想稍后将变量的名称存储到Console.Out.WriteLine()
  • 这里有许多基本错误。 One() 是一个实例方法,您正在从静态方法中访问this。你想打什么电话?实例方法One()或存储在main()one的局部变量中的委托?
  • @knittl 如果您只想存储变量的名称,为什么不直接使用“this.delegateMember.Method.Name”?这对我来说比做狡猾的反射黑客更有意义?
  • @jeff:感谢提及,已修复。我想调用存储在局部变量“one”(小写)中的委托

标签: c# .net string reflection delegates


【解决方案1】:

变量几乎存在。可靠地按字符串调用(在这种情况下)的唯一方法是将委托存储在字典中:

Dictionary<string, DemoDelegate> calls = new Dictionary<string, DemoDelegate>
{
    {"one",one}, {"two",two}
}

现在将该字典存储在某处(通常在字段中),然后执行以下操作:

private int CallDelegate(string name) {
    return calls[name].Invoke(); // <==== args there if needed
}

【讨论】:

  • 这完全不是真的。可以使用闭包在其本机上下文之外使用变量(无论名称是什么,在 C# 中它们被称为委托、lamdas 等)。
  • 这一事实与 linq 表达式相结合,使编译器“打印”我想要的关于变量的信息,使用 lamda: () => varIWishToGetInfo。
  • @Miguel 好吧,你可以...有时。不过不要从 F# 尝试它;p 实际上,我不认为我推荐它
【解决方案2】:

是的,这是可能的,只要你使用 Linq 表达式,并且很少反射。

看看这段代码,它的作用与我认为你想要的类似:

using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Collections.Generic;

namespace q6010555
{
    class Demo
    {
        static List<string> varNamesUsed = new List<string>();

        public delegate int DemoDelegate();

        private static int One()
        {
            return 1;
        }
        private static void CallDelegate(Expression<Func<DemoDelegate>> expr)
        {
            var lambda = expr as LambdaExpression;
            var body = lambda.Body;
            var field = body as MemberExpression;
            var name = field.Member.Name;
            var constant = field.Expression as ConstantExpression;
            var value = (DemoDelegate)((field.Member as FieldInfo).GetValue(constant.Value));

            // now you have the variable name... you may use it somehow!
            // You could log the variable name.
            varNamesUsed.Add(name);

            value();
        }
        static void Main(string[] args)
        {
            DemoDelegate one = Demo.One;
            CallDelegate(() => one);

            // show used variable names
            foreach (var item in varNamesUsed)
                Console.WriteLine(item);
            Console.ReadKey();
        }
    }
}

【讨论】:

  • 从哪里调用一个委托?
  • 我已经更改了我的答案,包括代表电话。但我还不明白,为什么你需要变量的名称。是用来记录的吗?
  • 或者,也许我误解了这个问题。
【解决方案3】:
public void Fire(string name)
        {
            FieldInfo field = this.GetType().GetField(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

            if (field != null)
            {
                Delegate method = field.GetValue(this) as Delegate;

                if (method != null)
                {
                    method.Method.Invoke(method.Target, new object[0]);
                }
            }
        }

显然限制您使用参数化委托。

【讨论】:

  • 这适用于存储在成员变量中的委托,但不适用于局部变量,如果我理解正确的话?
  • @knittl:如果您使用局部变量,您应该已经知道它们的名称。在这种情况下,您可以只使用 switchDictionary(这是 Marc 在 his answer 中所建议的。
  • @brian:函数调用前的局部变量。我知道然后 mase,但我的代码不知道 ;)
【解决方案4】:

您不能真正访问另一个堆栈帧中的变量(尽管我认为可以在StackFrame 类周围使用hackery)。相反,如果您想以类似反射的方式调用泛化委托,您将希望传递一个 Delegate 对象并使用 DynamicInvoke 之类的方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-30
    • 2020-09-11
    • 1970-01-01
    • 1970-01-01
    • 2011-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多