用于保存不同类型方法的变量类型(多重签名)

时间:2014-10-16 07:48:05

标签: c# vb.net methods delegates c#-2.0

我正在使用vb.net 2.0。我知道代表可以用来持有相同签名的不同功能。我可以用什么类型的多重签名方法?

我的情况是这样的。我需要创建一个项目列表及其相应的操作(方法)。 opertaion的签名可能各不相同。该列表将作为参数传递给另一个函数。该函数应该能够对列表中的每个项执行操作(方法)。保持最好输入变量的方法

谢谢和问候 Binesh nambiar C

2 个答案:

答案 0 :(得分:0)

基本委托类型可用于列表而不是特定委托类型。调用有点复杂:使用反射为委托类型获取编译器生成的Invoke()方法,然后可以通过MethodInfo对象调用它。当然,您需要一些机制来提供不同的参数列表。

另一种选择是将概括推回到调用者。使您的API存储成为Action列表,并要求调用者提供一个合适的委托实例,该实例接受调用的参数object [],并调用相应的强类型委托实例。

例如:

List<Action<object[]>> _delegateList;

void Target(string arg1, int arg2) { ... }

void Caller()
{
    AddDelegate(args => Target((string)args[0], (int)args[1]));
}

void AddDelegate(Action<object[]> callback)
{
    _delegateList.Add(callback);
}

然后是:

void InvokeDelegate(int i, object[] args)
{
    _delegateList[i](args);
}

从您的帖子中不清楚如何将此列表中的委托与需要传递给它的参数相关联,因此我对上面的内容进行了一些掩饰。大概你有一些合理的机制。

如果您在将代表添加到列表时知道参数列表,那么您当然不需要Action作为委托列表类型。它可以只是Action,您可以在&#34; Caller()&#34;中捕获初始lambda表达式中的参数值。方法

答案 1 :(得分:0)

我不确定你为什么要这样做:为什么不调用方法让编译器的重载决策算法弄明白?

无论如何,作为一个有趣的小练习(我今天很无聊)你可以构建类似于以下内容的东西(dynamic我不确定它是否有用的场景):

public class OverloadDelegateList
{
    readonly object target;
    readonly string methodName;
    List<InvokableMethod> methods;

    public OverloadDelegateList(Delegate firstOverload)
    {
        Debug.Assert(firstOverload != null);

        this.methods = new List<InvokableMethod>();
        this.target = firstOverload.Target;
        this.methodName = firstOverload.Method.Name;
        AddOverload(firstOverload);
    }

    public IEnumerable<InvokableMethod> InvokableMethods
    {
        get { return this.methods; }
    }

    public void AddOverload(Delegate d)
    {
        Debug.Assert(d != null);

        if (!Object.ReferenceEquals(d.Target, target))
            throw new ArgumentException();

        if (d.Method.Name != this.methodName)
            throw new ArgumentException();

        this.methods.Add(new InvokableMethod(d, (from p in d.Method.GetParameters() select p.ParameterType).ToArray()));
    }

    public object DynamicInvoke(params object[] args)
    {
        var signature = new MethodSignature((from a in args select a.GetType()).ToArray());
        var overload = this.methods.FirstOrDefault(m => m.Signature.Equals(signature));

        if (overload == null)
            throw new ArgumentException();

        return overload.InvokableOverload.DynamicInvoke(args);
    }
}

public class InvokableMethod
{
    readonly MethodSignature signature;
    readonly Delegate invokableMethod;

    public InvokableMethod(Delegate invokableMethod, params Type[] types)
        :this(invokableMethod, new MethodSignature(types))
    {
    }

    public InvokableMethod(Delegate invokableMethod, MethodSignature signature)
    {
        Debug.Assert(invokableMethod != null);

        this.invokableMethod = invokableMethod;
        this.signature =signature;
    }

    public Delegate InvokableOverload { get { return this.invokableMethod; } }
    public MethodSignature Signature { get { return this.signature; } }
}

public class MethodSignature: IEquatable<MethodSignature>
{
    readonly List<Type> signature;

    public MethodSignature(params Type[] types)
    {
        this.signature = types.ToList();
    }

    public IEnumerable<Type> Signature { get { return this.signature; } }

    public override bool Equals(object obj)
    {
        return this.Equals(obj as MethodSignature);
    }

    public bool Equals(MethodSignature other)
    {
        if (object.ReferenceEquals(other, null))
            return false;

        if (other.signature.Count != this.signature.Count)
            return false;

        for (int i = 0; i < this.signature.Count; ++i)
        {
            if (this.signature[i] != other.signature[i])
                return false;
        }

        return true;
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int hash = 0;

            if (this.signature != null)
            {
                foreach (var t in this.signature)
                {
                    hash ^= t.GetHashCode();
                }
            }

            return hash;
        }
    }
}

您可以按如下方式使用:

考虑以下类Foo为方法Bar实现5个不同的重载:

public class Foo
{
    public void Bar() { Console.WriteLine("Bar() called: {0}", string.Empty); }
    public void Bar(int i) { Console.WriteLine("Bar(int) called: {0}", i); }
    public void Bar(double d) { Console.WriteLine("Bar(double) called: {0:N4}", d); }
    public void Bar(int i, string s) { Console.WriteLine("Bar(int, string) called: {0} & {1}", i, s); }
    public void Bar(double d, string s) { Console.WriteLine("Bar(double, string) called: {0:N4}, {1}", d, s); }
}

您可以建立一个列表,其中包含Foo.Bar()重载的所有信息,如下所示:

 var foo = new Foo();
 var overloads = new OverloadDelegateList(new Action(foo.Bar));
 overloads.AddOverload(new Action<int>(foo.Bar));
 overloads.AddOverload(new Action<double>(foo.Bar));
 overloads.AddOverload(new Action<int, string>(foo.Bar));
 overloads.AddOverload(new Action<double, string>(foo.Bar));

现在,您可以根据运行时编号和参数类型动态调用正确的重载:

//We want to call Bar overload with one argument.
object arg1 = 1d;
overloads.DynamicInvoke(arg1);

//We want to call Bar overload with two arguments.
object arg2 = "s";
overloads.DynamicInvoke(arg1, arg2);

请注意,要使此解决方案正常工作,您需要具备foo(被调用者)的静态知识。如果您对被调用者没有静态知识,那么您将需要使用Reflection来确定可能的重载。