4

is it possible to call a delegate stored in a variable by its variable name (as a string)? i guess i'd have to use reflection mechanism, but i'm not getting anywhere

example code:

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);
  }

}

is this even possible? if so how? if not, then i have to use the second form, bad luck

knittl
  • 246,190
  • 53
  • 318
  • 364
  • Having `DemoDelegate d` in the second overload completely defeats the purpose of `string name`, doesn't it? – BoltClock May 15 '11 at 19:08
  • @boltclock: yes, that's why i want to avoid it (in my real code, i want to store the name of the variable to `Console.Out.WriteLine()` it later) – knittl May 15 '11 at 19:14
  • There are a number of fundamental errors here. `One()` is an instance method for one and you're accessing `this` from within a static method. What are you trying to call? The instance method `One()` or the delegate stored in the local variable in `main()`, `one`? – Jeff Mercado May 15 '11 at 19:24
  • @knittl if you only want to store the name of the variable, why not just use "this.delegateMember.Method.Name"? this makes more sense to me than doing dodgy reflection hacks? – peteisace May 15 '11 at 19:26
  • @jeff: thanks for mentioning, fixed it. and i want to call the delegate stored in the local variable "one" (lowercase) – knittl May 15 '11 at 19:28
  • @knittl: Maybe I'm missing the point here but you already have an instance of your delegate in `one`. You don't need its name to be able to invoke it. If you needed to access the `One()` method from a different class, that would be a different case however. – Jeff Mercado May 15 '11 at 19:31
  • @jeff: this is only example code, to make it clear i want to call a delegate (and not a method directly) – knittl May 15 '11 at 19:39
  • I have changed my answer to write the variable name, to the console, later, as you suggested in a previous comment. – Miguel Angelo May 15 '11 at 19:54

4 Answers4

6

Variables barely exist. The only way to reliably call-by-string (in this scenario) would be to store the delegates in a dictionary:

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

Now store that dictionary somewhere (in a field, typically), and do something like:

private int CallDelegate(string name) {
    return calls[name].Invoke(); // <==== args there if needed
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • This is quite not true. It is possible to use variable outside their native contexts using closures (whatever the name is, in C# they are called delegates, lamdas, etc..). – Miguel Angelo May 15 '11 at 19:53
  • This fact combined with linq expressions, makes the compiler "print" info I want about a variable, using the lamda: () => varIWishToGetInfo. – Miguel Angelo May 15 '11 at 20:05
  • @Miguel well, you can... sometimes. Don't try it from F# though ;p In reality, I don't think I recommend it – Marc Gravell May 15 '11 at 21:52
3

Yes, it is possible, as long as you use Linq Expressions, and little reflection.

Take a look at this code, it does something simillar to what I think you want:

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();
        }
    }
}
Miguel Angelo
  • 23,796
  • 16
  • 59
  • 82
1
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]);
                }
            }
        }

Obviously restricts you from having parameterized delegates.

peteisace
  • 2,754
  • 1
  • 19
  • 19
  • this works for delegates stored in member variables, but not for local variables, if i understand correctly? – knittl May 15 '11 at 19:32
  • @knittl: If you're using local variables, you should **know** their name already. In that case, you could just use a `switch` or a `Dictionary` (which is what Marc suggests in [his answer](http://stackoverflow.com/questions/6010555/how-to-call-delegate-from-string-in-c/6010611#6010611). – Brian May 16 '11 at 14:39
  • @brian: local variables before the function call. i know then mase, but my code does not ;) – knittl May 16 '11 at 17:03
0

You can't really access variables in another stack frame (although I think it is possible using hackery around the StackFrame class). Instead, you'll be wanting to pass an Delegate object around and use methods like DynamicInvoke, if you want to invoke a generalised delegate in a reflection-like manner.

thecoop
  • 45,220
  • 19
  • 132
  • 189