【问题标题】:Variable type to hold different type of methods(multi signature)变量类型来保存不同类型的方法(多签名)
【发布时间】:2014-12-11 11:56:47
【问题描述】:

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

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

感谢和问候 Binesh nambiar C

【问题讨论】:

  • 听起来你想要的是一个重载的方法

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


【解决方案1】:

基本委托类型可用于您的列表,而不是特定委托类型。调用有点复杂:使用反射来获取委托类型的编译器生成的 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,您可以在“Caller()”方法中捕获初始 lambda 表达式中的参数值。

【讨论】:

    【解决方案2】:

    我不确定您为什么要这样做:为什么不调用该方法让编译器的重载解析算法来解决这个问题?

    无论如何,作为一个有趣的小练习(我今天很无聊),您可以构建类似于以下内容的东西(有 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 来找出可能的重载。

    【讨论】:

    • 为什么我需要它是因为函数的调用将在大量操作之后进行,这些操作将在决定需要发生哪些操作之后动态(可能多次)。
    猜你喜欢
    • 1970-01-01
    • 2017-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多