Лучший способ избежать повторения кода с помощью Generics и Func в С#

Я хотел бы знать, как лучше всего избежать повторения некоторой повторяющейся структуры кода с помощью Generics Func или любым другим способом. В качестве практического примера давайте мне нужно вызвать 20 различных методов WCF, но я хотел бы иметь код для обработки исключений.

Допустим, это прокси wcf

 class ClassWithMethodsToCall // say wcf proxy
    {
        public Out1 GetOut1(In1 inParam) { return null; } // would have some spesific implementation 
        public Out2 GetOut2(In2 inParam) { return null; }
        public Out3 GetOut3(In3 inParam) { return null; }
    }

class Out1 { }  // some specific data structure
class In1 { }   // some specific data structure

class Out2 { }  // some specific data structure
class In2 { }   // some specific data structure

class Out3 { }  // some specific data structure
class In3 { }   // some specific data structure

Я создал следующее, чтобы иметь одну обработку ошибок

class CallerHelperWithCommonExceptionHandler
    {
        public Tout Call<Tout, Tin>(Tin parameters, Func<Tin,Tout> wcfMethodToCall)
        {
            try
            {
                return wcfMethodToCall(parameters);
            }
            catch (Exception ex)
            {
                // do what ever
                throw;
            }
        }
    }

И я использую его:

var callerHelper = new CallerHelperWithCommonExceptionHandler();
            var theFunctionsToCall = new ClassWithMethodsToCall();

        var in1 = new In1(); // init as appropriate
        var ou1 = callerHelper.Call<Out1, In1>(in1, theFunctionsToCall.GetOut1);

        var in2 = new In2(); // init as appropriate
        var ou2 = callerHelper.Call<Out2, In2>(in2, theFunctionsToCall.GetOut2);

        // and so on

Есть ли лучший более элегантный способ? Альтернативы объектно-ориентированному подходу, шаблон проектирования шаблонов?

Спасибо, аль


person user2418209    schedule 19.02.2015    source источник
comment
Это вовсе не повторение. Ваш класс ClassWithMethodsToCall мне подходит. Ваш CallerHelperWithCommonExceptionHandler усугубляет ИМО.   -  person Sriram Sakthivel    schedule 19.02.2015
comment
Согласен, выглядит гораздо хуже.   -  person BenjaminPaul    schedule 19.02.2015
comment
То, что вы ищете, может быть Аспектно-ориентированным программированием, коротким АОП, вот пример ayende. com/blog/3474/logging-the-aop-way   -  person BoeseB    schedule 19.02.2015
comment
Что ж, без CallerHelperWithCommonExceptionHandler мне приходится повторять try catch для каждого вызова метода ClassWithMethodsToCall . Если бы у меня было 20 методов, я бы повторил один и тот же блок кода обработки ошибок для каждого из них.   -  person user2418209    schedule 19.02.2015


Ответы (1)


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

Например:

class Out1 { };  // some specific data structure
class In1 { }   // some specific data structure

class Out2 { }  // some specific data structure
class In2 { }   // some specific data structure

class Out3 { }  // some specific data structure
class In3 { }

internal interface IClassWithMethodsToCall
{
    Out1 GetOut1(In1 inParam);
    Out2 GetOut2(In2 inParam);
    Out3 GetOut3(In3 inParam);
}

class ClassWithMethodsToCallImpl: IClassWithMethodsToCall
{
    public Out1 GetOut1(In1 inParam) { return null; } // would have some spesific implementation 
    public Out2 GetOut2(In2 inParam) { return null; }
    public Out3 GetOut3(In3 inParam) { return null; }
}

class ClassWithMethodsToCall: IClassWithMethodsToCall
{
    private readonly ClassWithMethodsToCallImpl _impl;

    public ClassWithMethodsToCall(ClassWithMethodsToCallImpl impl)
    {
        _impl = impl;
    }

    public Out1 GetOut1(In1 inParam)
    {
        return tryFunc(() => _impl.GetOut1(inParam));
    }

    public Out2 GetOut2(In2 inParam)
    {
        return tryFunc(() => _impl.GetOut2(inParam));
    }

    public Out3 GetOut3(In3 inParam)
    {
        return tryFunc(() => _impl.GetOut3(inParam));
    }

    private static T tryFunc<T>(Func<T> func)
    {
        try
        {
            return func();
        }

        catch (Exception exception)
        {
            // Do something with exception
            throw;
        }
    }
}

Клиентский код будет использовать только IClassWithMethodsToCall, и вы, вероятно, будете использовать фабричный метод где-нибудь, который создает ClassWithMethodsToCallImpl и использует его для создания ClassWithMethodsToCall и возвращает ClassWithMethodsToCall как IClassWithMethodsToCall.

В качестве альтернативы (и, вероятно, лучше) было бы использовать аспектно-ориентированное программирование. Это требует дополнительных инвестиций и, возможно, использования сторонних библиотек для его поддержки, но, вероятно, это путь в долгосрочной перспективе.

Я заметил, что вы используете прокси-сервер WCF. Поскольку для этого используется MarshalByRefObject, вы можете использовать его для реализации АОП. Некоторая информация об этом есть в блоге здесь .

person Matthew Watson    schedule 19.02.2015
comment
@user2418209 user2418209 Да, это по сути то же самое, что и private static T tryFunc<T>(Func<T> func), которое я показал — вы можете использовать этот подход для реализации логики повторных попыток, поместив повторные попытки внутри tryFunc() - person Matthew Watson; 19.02.2015
comment
@user2418209 user2418209 АОП не ограничивается только регистрацией/обработкой исключений, можно реализовать, например, кэширование, обработку транзакций и многое другое. Как раз все, что нужно для перехвата вызовов методов объектов. поэтому логика повторных попыток также возможна в АОП. - person BoeseB; 20.02.2015