views:

252

answers:

2

In C#, is it possible to have an object that has multiple method signatures for an Action<> or delegate? Like this:

class Foo
{
    public Action<string> DoSomething;
    public Action<string, string> DoSomething;
}

class Bar
{
    public Bar()
    {
        Foo f1 = new Foo();
        f1.DoSomething = (s) => { Console.Write(s) };
        Foo f2 = new Foo();
        f2.DoSomething = (s1, s2) => { Console.Write(s1 + s2) };

        f1.DoSomething("Hi");
        f2.DoSomething("Hi","World");
    }
}

The answer seems to be no, so what is the proper way to implement something like that? (The actual problem this was trying to solve has been solved a different way, this is just curiosity at this point)

+3  A: 

A delegate is an abstraction of a single method (of course, several methods with similar signatures can be represented by a single delegate but from the caller's perspective, it behaves just like a single method, so that's irrelevant here.) It doesn't make sense for a single method to have multiple signatures. Hence, a delegate instance has a specific signature. Overload resolution does not have any meaning for delegates. It's not a method group you're choosing from. You're directly pointing to a method and saying "call this."

What's the solution to this problem?

It's not clear to me what the actual problem is. This might be what you're looking for:

class Foo {
    public Action<string> DoSomethingDelegate1;
    public Action<string,string> DoSomethingDelegate2;
    public void DoSomething(string s) { DoSomethingDelegate1(s); }
    public void DoSomething(string s, string t) { DoSomethingDelegate2(s, t); }
}

class Bar
{
    public Bar()
    {
        Foo f1 = new Foo();
        f1.DoSomethingDelegate1 = (s) => { Console.Write(s) };
        Foo f2 = new Foo();
        f2.DoSomethingDelegate2 = (s1, s2) => { Console.Write(s1 + s2) };

        f1.DoSomething("Hi");
        f2.DoSomething("Hi","World");
    }
}
Mehrdad Afshari
That's actually pretty close to what I was looking for (and what I ended up doing), my question was more of was there a way to avoid the extra set of functions (the "DoSomething"s) being declared. I didn't believe there was, but I wanted to verify it.
Dugan
A: 

The Delegate type is abstract and only delegates based on fully typed signatures can be created. Thus it is impossible to just create a delegate to any method without providing a Delegate template, but it is still possible to assign using an existing delegate type, such as Action or Action<T>:

class Foo
{
    public Delegate Target { get; set; }

    public void Fire()
    {
     if (Target != null)
     {
      var pinfos = Target.Method.GetParameters();
      object[] args = new object[pinfos.Length];
      for (int i = 0; i < pinfos.Length; i++)
      {
       // Attempt to create default instance of argument:
       args[i] = Activator.CreateInstance(pinfos[i].ParameterType);
      }
      Target.DynamicInvoke(args);
     }
    }
}

class Bar
{
    public void Huppalupp()
    {
     Foo f = new Foo();
     f.Target = new Action(MethodThatTakesNothing);
     f.Fire();
     f.Target = new Action<string>(MethodThatTakesAString);
    }

    void MethodThatTakesNothing()
    {
        Console.WriteLine("Absolutely nothing.");
    }

    void MethodThatTakesAString(string s)
    {
        Console.WriteLine(s);
    }
}

This compiles, but I haven't tried it for any purposeful purpose.

Cecil Has a Name