Вызвать делегат по его имени
От: BDEsoft  
Дата: 11.12.07 07:50
Оценка:
Здравствуйте, возникла такая проблема.
Есть библиотека, выполняющая функции калькулятора. Ей задаются строки вида "a=5"(запомнить переменную с именем a и значение 5), "4+a"(возвратить результат 4+5) и т.д.
Необходимо сделать следующее, ввести стоку примера "apt(x,y)=aptfunc"(где apt — имя функции для запоминания, (x,y) — ее аргументы, aptfunc — имя делегата функции". Сам делегат определен в программе, вызывающей эту библиотеку:
    class pr1
    {
        public static int apt(int x, int y)
        {
            return x * y;
        }
    }
    class Program
    {
        public static CalcLib.CalcLib cl = new CalcLib.CalcLib();
        delegate int aptdelg(int x,int y);

        static void Main(string[] args)
        {
            aptdelg aptfunc = new aptdelg(pr1.apt);
            cl["apt(x,y)"] = "aptfunc";
            int result=cl["apt(3,4)"];    
        }

для получения результата result калькулятору отправляется строка "apt(3,4)", по которой она должна вызвать метод pr1.apt с аргументами 3 и 4.
так вот вопрос, как из библиотеки вызвать метод p1.apt с аргументами (3,4) имея только текстовое название делегата "aptfunc", может чтото не так делаю, может нужно задавать строку так: "apt(x,y)=aptdelg", я в этом плохо понимаю...
Спасибо за ответы.
Re: Вызвать делегат по его имени
От: Ziaw Россия  
Дата: 11.12.07 08:50
Оценка:
Здравствуйте, BDEsoft, Вы писали:

BDE>так вот вопрос, как из библиотеки вызвать метод p1.apt с аргументами (3,4) имея только текстовое название делегата "aptfunc", может чтото не так делаю, может нужно задавать строку так: "apt(x,y)=aptdelg", я в этом плохо понимаю...

если aptfunc локальная переменная — никак.

функцию p1.apt вызвать через рефлекшн проблем нет, зачем понадобилось ссылаться на локальный делегат — непонятно, т.е. я не вижу вероятных сценариев использования.
... << RSDN@Home 1.2.0 alpha rev. 786>>
Re: Вызвать делегат по его имени
От: _Morpheus_  
Дата: 12.12.07 20:39
Оценка:
Здравствуйте, BDEsoft, Вы писали:

BDE>так вот вопрос, как из библиотеки вызвать метод p1.apt с аргументами (3,4) имея только текстовое название делегата "aptfunc", может чтото не так делаю, может нужно задавать строку так: "apt(x,y)=aptdelg", я в этом плохо понимаю...

BDE>Спасибо за ответы.


Ну делегат тут не удачный выбор, такие вещи делаются примерно так:
using System;
using System.Collections;
using System.Collections.Generic;

public abstract class Operation
{
    public abstract int ArgumentCount { get; }

    public abstract int Do(int[] arguments);

    protected virtual void checkArguments(int[] arguments)
    {
        if (arguments.Length != ArgumentCount)
            throw new ArgumentException(GetType().ToString() + " требует " + ArgumentCount.ToString() + " аргументов, но передано " + arguments.ToString() + "!");
    }
}

public class AddOperation : Operation
{
    public override int ArgumentCount { get { return 2; } }

    public override int Do(int[] args)
    {
        checkArguments(args);
        return args[0] + args[1];
    }
}

public class SubOperation : Operation
{
    public override int ArgumentCount { get { return 2; } }

    public override int Do(int[] args)
    {
        checkArguments(args);
        return args[0] - args[1];
    }
}

public class AptFuncOperation : Operation
{
    public override int ArgumentCount { get { return 2; } }

    public override int Do(int[] args)
    {
        checkArguments(args);
        return args[0] * args[1];
    }
}

public class Calculator
{
    private Stack _stack = new Stack();
    private Dictionary<string, Operation> _operations = new Dictionary<string, Operation>();

    public Calculator()
    {
        _operations["+"] = new AddOperation();
        _operations["-"] = new SubOperation();
        _operations["aptfunc"] = new AptFuncOperation();
    }

    public void PushOperand(int value)
    {
        _stack.Push(value);
    }

    public void PushOperation(string name)
    {
        if (!_operations.ContainsKey(name))
            throw new ArgumentException("Неизвестная операция: " + name);
        _stack.Push(_operations[name]);
    }

    public int Calculate()
    {
        if (_stack.Count < 1)
            throw new InvalidOperationException("Нет данных для вычислений");

        object val = _stack.Pop();
        Operation op = val as Operation;
        if (op == null)
            return (int)val;

        int argCount = op.ArgumentCount;

        int[] args = new int[op.ArgumentCount];
        for (int i = 0; i < args.Length; i++)
        {
            if (_stack.Count < argCount)
                throw new ArgumentException("Недостаточно аргументов для операции " + op.GetType().ToString());

            if (_stack.Peek() is Operation)
                args[i] = Calculate();
            else
                args[i] = (int)_stack.Pop();
            argCount--;
        }
        return op.Do(args);
    }
}


// Тестируем то что получилось :)
class Program
{
    static void Main()
    {
        Calculator calc = new Calculator();

        // Вычисляем "aptfunc(2, 2)" [ aptfunc(2, 2) = 4 ]
        calc.PushOperand(2);
        calc.PushOperand(2);
        calc.PushOperation("aptfunc");
        Console.WriteLine("aptfunc(2, 2) = {0}", calc.Calculate());


        // Вычисляем "aptfunc(30-5, 3+2)" [ aptfunc(30-5, 3+2) = aptfunc(25, 5) = 125 ]
        calc.PushOperand(2);
        calc.PushOperand(3);
        calc.PushOperation("+");
        calc.PushOperand(5);
        calc.PushOperand(30);
        calc.PushOperation("-");
        calc.PushOperation("aptfunc");
        Console.WriteLine("aptfunc(30-5, 3+2) = {0}", calc.Calculate());
    }
}


Тебе остается дописать токенайзер и транслятор в обратную польскую запись, с этим думаю справишься. Ну и если надо с разными типами данных работать, то напиши свой класс представляющий значение и замени int на этот класс...

Вот собственно и всё
... << RSDN@Home 1.2.0 alpha rev. 676>>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.