четверг, 6 декабря 2012 г.

Пример обертки над внешними методами с применением Reflection

Это тпример написан на вопрос с форумов MSDN. Итак, стоит задача, написать класс, в который можно подгружать методы из внешних сборок и вызывать их по мере необходимости.

Чтобы задача была интересная, давайте добавлю требование, чтобы вызов методов мог осуществляться в режиме Singlecall (при каждом вызове метода объект создается заново) и в режиме Singleton (при первом вызове объект создается, все последующие вызовы идут к этому же экземпляру).
Начну с классов, которые буду вызывать. Создав решение, я добавил в него две dll:
В первой сделал заготовку класса для вызова в режиме Singleton:

namespace ClassLibrary1
{
    public class Class1
    {
        int a = 0;
 
        public int IncAndReturn()
        {
            a++;
            return a;
        }
    }
}

Во второй, для вызова в режиме Singlecall:

namespace ClassLibrary2
{
    public class Class1
    {
        public int Sum(int a, int b)
        {
            return a + b;
        }
    }
}

Хорошо, теперь, приведу код, который будет использоваться для вызова:

static void Main(string[] args)
{
    Executor executor = new Executor();
    executor.AddMethod("First_Singleton""classlibrary1.dll""ClassLibrary1.Class1""IncAndReturn"true);
    executor.AddMethod("Second_Singlecall""classlibrary2.dll""ClassLibrary2.Class1""Sum"false);
 
    Console.WriteLine(executor.Execute<int>("First_Singleton"));
    Console.WriteLine(executor.Execute<int>("First_Singleton"));
    Console.WriteLine(executor.Execute<int>("First_Singleton"));
    Console.WriteLine(executor.Execute<int>("Second_Singlecall", 2, 4));
    Console.WriteLine(executor.Execute<int>("Second_Singlecall", 3, 1));
    Console.ReadKey();
}

Метод для добавления метода к классу, принимает псевдоним, путь к dll, полное имя класса, имя метода и тип вызова true - Singleton. Метод для вызова, принимает только псевдоним и параметры, если они необходимы. Т.к. вызываемый метод будет возвращать значение, мы еще и указываем, какого оно будет типа (в моем примере int).
Собственно, начинаем писать класс Executor. Для начала, определим класс, для хранения информации о методах:

class MethodWrapper
{
    public MethodInfo Method { getset; }
    public object Object { getset; }
    public ConstructorInfo Constructor { getset; }
    public bool IsSingleton { getset; }
}

В классе Executor, объявим поле и напишем конструктор:

Dictionary<stringMethodWrapper> _methods = null;
 
public Executor()
{
    _methods = new Dictionary<stringMethodWrapper>();
}

Напишем метод для добавления методов:

public void AddMethod(string p_methodAlias, string p_dllPath, string p_classFullName, string p_methodName, bool p_isSingleton)
{
    Assembly a = Assembly.LoadFrom(p_dllPath);
    if (a == null)
    {
        throw new Exception("Ошибка загрузки dll");
    }
    Type t = a.GetType(p_classFullName);
    if (t == null)
    {
        throw new Exception("Ошибка извлечения класса");
    }
    MethodInfo mi = t.GetMethod(p_methodName);
    if (mi == null)
    {
        throw new Exception("Ошибка извлечения метода");
    }
    ConstructorInfo ci = t.GetConstructor(Type.EmptyTypes);
    if (ci == null)
    {
        throw new Exception("Ошибка извлечения конструктора по умолчанию");
    }
    MethodWrapper wrapper = new MethodWrapper() { 
        Constructor = ci,
        IsSingleton = p_isSingleton,
        Method = mi
    };
    _methods.Add(p_methodAlias, wrapper);
}

Ну и метод для вызова:

public T Execute(string p_methodAlias, params object[] p_params)
{
    MethodWrapper wrapper = _methods[p_methodAlias];
    object o = null;
    if (wrapper.IsSingleton)
    {
        if (wrapper.Object == null)
        {
            wrapper.Object = wrapper.Constructor.Invoke(new object[0]);
        }
        o = wrapper.Object;
    }
    else
    {
        o = wrapper.Constructor.Invoke(new object[0]);
    }
    return (T)wrapper.Method.Invoke(o, p_params);
}

Как видно, в зависимости от значения свойства IsSingleton у нас происходит "ленивая" инициализация свойства Object или объект создается каждый раз.
Запускаем программу:
Напомню, что первые три вызова идут к Singleton-у, в котором происходит инкримент поля, а два последних значения получаются суммированием. Кстати, если изменить тип вызова первого метода на Singlecall:

executor.AddMethod("First_Singleton""classlibrary1.dll""ClassLibrary1.Class1""IncAndReturn"false);

То вывод будет отличаться, т.к. при инкрименте, каждый раз будет создаваться новый объект:
Все.

Комментариев нет:

Отправить комментарий