2016-01-30 4 views
1

Моя цель - иметь методы init() и complete(result), которые будут работать до и после каждого метода выбранного класса. Аналогично тому, как ActionFilterAttribute в ASP.NET имеет методы OnActionExecuting и OnActionExecuted, которые запускаются до и после любого метода, к которому вы их применяете.Запуск определенного метода до и после каждого вызова реализации интерфейса

Существует несколько интерфейсов. Я хотел бы применить те же методы init() и complete(result), и я хочу избежать дублирования кода и упростить код, если это возможно.

Из того, что я могу сказать, похоже, нет элегантного решения. До сих пор у меня есть 3 варианта:

Вариант 1:

public interface MyImportantMethods 
    { 
     object f(); 
     object g(); 
    } 

    public class MyClass : MyImportantMethods 
    { 
     public object f() 
     { 
      // Do things 
     } 

     public object g() 
     { 
      // Do things 
     } 
    } 

    public class WrappedMyClass : MyImportantMethods 
    { 
     private MyImportantMethods ActualContent; 

     public MyModifiedClass(MyImportantMethods actualContent) 
     { 
      this.ActualContent = actualContent; 
     } 

     public object f() 
     { 
      init(); 
      object result = this.ActualContent.f(); 
      complete(result); 
      return result; 
     } 

     public object g() 
     { 
      init(); 
      object result = this.ActualContent.g(); 
      complete(result); 
      return result; 
     } 
    } 
  • Плюсы: Класс с реализацией полностью отделено от тот, который вызывает init() и complete(), поэтому более читаемый и проще понять.
  • Против: для кого-то, не знакомого с кодом, использовать не тот. Требуется другой класс для каждого интерфейса, который я хочу использовать .

Вариант 2:

public interface MyImportantMethods 
    { 
     object f(); 
     object g(); 
    } 


    class Wrapper: IDisposable 
    { 
     public object result; 
     public Wrapper() 
     { 
      init(); 
     } 

     void Dispose() 
     { 
      complete(result); 
     } 
    } 

    public class MyModifiedClass { 
     private void f() 
     { 
      using (var wrapper = new Wrapper(() => completeF())) 
      { 
       // Do Things 
       wrapper.result = result; 
      } 
     } 

     private void g() 
     { 
      using (var wrapper = new Wrapper(() => completeG())) 
      { 
       // Do Things 
       wrapper.result = result; 
      } 
     } 
    } 
  • Плюсы: Не возможно использовать неправильный класс. Может быть, можно использовать один IDisposable класс для всех интерфейсов, если я использую это отражение трюк

  • Минусы: Код будет выглядеть гораздо более суматоху и трудно понять, для нового читателя, особенно если декларация обертки принимает множественным строк или обертки требуется больше одного параметра из результата (оба значения верны в моем случае). Также полагается на вызывающего абонента, чтобы установить результат.

Вариант 3:

public abstract class MyImportantMethodsBase 
    { 
     public object f() 
     { 
      init(); 
      object result = this.fImplementation(); 
      complete(fResult); 
      return result; 
     } 

     public object g() 
     { 
      init(); 
      object result = this.gImplementation(); 
      complete(result); 
      return result; 
     } 

     private abstract object fImplementation(); 

     private abstract object gImplementation(); 
    } 

    public class MyModifiedClass : MyImportantMethodsBase { 
     private object void fImplementation() 
     { 
      // Do Things 
     } 

     private object gImplementation() 
     { 
      // Do Things 
     } 
    } 
  • Плюсы: Не возможно использовать неправильный класс. Класс с реализацией полностью отделен от той, которая вызывает init() и complete(), поэтому она более понятна и понятна.

    Против: Менее просто для чтения читателя.

Есть ли действительно никакого способа, как просто и понятно, как ActionFilterAttribute для достижения своей цели?

+0

ИМХО, вариант номер 3 является лучшим, и я не считаю, что это трудно понять. Как вы указываете ActionFilterAttribute, почему бы не создать атрибут и не использовать его в своих классах? – Skaparate

+0

Мне также нравится Option3: пример шаблона шаблона шаблона, который многие программисты распознают – alexm

+0

@Skaparate вам нужно что-то, что будет использовать атрибуты - вы не можете ожидать, что только добавление атрибутов пары будет волшебным образом реализовать перехват метода/интерфейса, доступный в большинстве контроллеры инъекций. Если вас интересует то, что OP, вероятно, пытается сделать, вы можете проверить, как это делается с Unity (https://msdn.microsoft.com/en-us/library/dn178466(v=pandp.30).aspx) или вообще https://www.bing.com/search?q=c%23%20dependency%20injection%20interception –

ответ

0

Возможно, вариант 1 с Factory Pattern достаточно: сделайте конструктор закрытым, чтобы его нельзя было использовать неправильно.Создайте статическую функцию в том же классе для создания объекта-объекта, а затем создайте вокруг него объект-оболочку, возвращая тип, являющийся общим общим интерфейсом.

Однако, если у вас есть много классов для обертывания, генерация кода времени выполнения (или генерация кода времени разработки .tt) может помочь вам. вызов WrapperGenerator.Create (newObject), где T является интерфейсом, а newObject - это частный построенный объект, который реализует этот интерфейс. Функция Create отражает интерфейс, а затем вы создаете новый класс «на лету» на основе этого интерфейса, который будет обертываться дополнительными вызовами функций вокруг функций, найденных одним базовым объектом. Любые вновь созданные классы-оболочки будут кэшироваться для одного и того же интерфейса.

class WrapperGenerator 
{ 
    static Dictionary<Type,ConstructorInfo> cachedWrappers = new ...(); 
    public static T Create<T>(object wrappedObject) 
    { 
     ConstructorInfo wrapperConstructor = null; 
     var found = cachedWrappers.TryGet(typeof(T), out wrapperConstructor); 
     if (found) 
     { 
      return (T)wrapperConstructor.Invoke(wrappedObject); 
     } 

     //cachedWrapper not found 
     wrapperConstructor = GenerateNewConstructor(typeof(T)); 
     cachedWrappers.Add(typeof(T), wrapperConstructor); 

     return (T)wrapperConstructor.Invoke(wrappedObject); 
    } 

    static long Counter = 0; 
    static ConstructorInfo GenerateNewConstructor(Type t) 
    { 
     var methodList = t.GetMethods(...); 
     StringBuilder sb = new StringBuilder(); 
     sb.Append("namespace WrapperClasses {\r\n"); 
     var ClassName = "Wrapper" + Counter++; 
     var InterfaceName = t.FullName; 
     sb.AppendFormat("public class {0} {{\r\n", ClassName); 
     sb.AppendFormat("{0} wrappedObject = null;\r\n", ClassName); 
     sb.AppendFormat("public Wrapper{0}({1} wrappedObject) {"\r\n, ClassName, InterfaceName); 
     sb.Append(" this.wrappedObject = wrappedObject;\r\n"); 
     sb.Append("}\r\n"); 
     foreach (var m in methodList) 
     { 
      sb.AppendFormat("public {0} {1}({2}) {{", m.ReturnType.., m.Name, ParameterDeclarationsSegment(m)); 
      sb.AppendFormat(" init();\r\n"); 
      sb.AppendFormat(" var result = wrappedObject.{0}({1});\r\n", m.Name, ParameterPassthroughSegment(m));  
      sb.AppendFormat(" complete(result);\r\n"); 
      sb.Append("};\r\n");  
     } 
     //ETC.. closing class and namespace 
     var LoadedAssembly = Compiler.Compile(sb,...); 
     var getWrapperType = LoadedAssembly.SearchType(ClassName); 
     return getWrapperType.GetConstructors()[0]; 
    } 
} 

Примечание: Вы должны применять блокировку вокруг cachedWrappers.

Смежные вопросы