Тип переменной для хранения различных типов методов (мультиподпись)

1

Я использую vb.net 2.0. Я знаю, что делегаты могут использоваться для выполнения различных функций одной и той же подписи. Какой тип я могу использовать для методов множественной подписи?

Мое дело так. Мне нужно создать список элементов и соответствующие им операции (Методы). Подпись opertaion может отличаться для каждого. Этот список будет передан как параметр другой функции. Эта функция должна иметь возможность выполнять операции (методы) с каждым элементом в списке. Удерживать методы, которые лучше всего подходят для переменной типа

Спасибо и с уважением Binesh nambiar C

  • 1
    Похоже, что вы хотите, это перегруженный метод
Теги:
delegates
methods
c#-2.0

2 ответа

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 реализующий 5 различных перегрузок для метода Bar:

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 (calllee). Если у вас нет статического знания об этом, вам нужно будет использовать Reflection, чтобы выяснить возможные перегрузки.

  • 0
    Почему мне это нужно, потому что вызов функции будет после большого количества операций, которые будут динамически (может многократно) после решения, какая операция должна произойти.
0

Тип базового делегата может использоваться для вашего списка вместо определенных типов делегатов. Вызов немного сложнее: используйте рефлексию для получения генерируемого компилятором метода Invoke() для типа делегата, который вы можете вызвать через объект MethodInfo для него. И, конечно, вам понадобится механизм для предоставления переменных списков аргументов.

Другой вариант - вернуть генерацию обратно вызывающему абоненту. Сделайте свой API-хранилищем список действий и попросите вызывающего пользователя предоставить подходящий экземпляр делегата, который принимает объект [] аргументов для вызова, и вызывает соответствующий строго типизированный экземпляр делегата.

Например:

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, и вы можете зафиксировать значения аргументов в своем исходном лямбда-выражении в методе "Caller()".

Ещё вопросы

Сообщество Overcoder
Наверх
Меню