Как скомпилировать строковые условия в аспектах - PullRequest
1 голос
/ 05 января 2012

У меня есть простой класс. И я должен добавить некоторые условия к некоторым методам и свойствам. Например:

public class Example
{
  public Boolean Condition {get; set;}
  public Double ConditionValue {get; set;}

  [Verify("!Condition && ConditionValue>5")]
  public void DoSomthing()
  { ... }
}

Я хочу проверить условие (например, «! Condition && ConditionValue> 5») в атрибуте аспекта. Я не могу дать действие / функцию в атрибут, поэтому я даю простую строку. И мне нужно перевести эту строку в условие:

[Serializable]
public class MyAspectAttribute : OnMethodBoundaryAspect
{
    public override void OnExit(MethodExecutionArgs args)
    {
        if (!this.Condition && this.ConditionValue>5) // If-statment from string, this is the problem...
        { ... }
    }
}  

Как я могу извлечь реальный if-statment из строки? Я вижу некоторые решения, но я не уверен, что они хороши:

  1. Использовать компилятор времени выполнения, например, CSharpCodeProvider.
  2. Используйте библиотеку, такую ​​как http://flee.codeplex.com/
  3. Что-то еще ...?

Как я могу это сделать изящно? Спасибо!

Upd: я отредактировал вопрос по советам ... Upd2: я не пытаюсь проверить мой код, я могу сделать это:

public class Example2
    {
      public Boolean Condition {get; set;}

      [LoggingIf("Condition")]
      public void DoSomthing1()
      { ... }

      [PrintReportIf("!Condition")]
      public void DoSomthing2()
      { ... }
    }

Ответы [ 6 ]

3 голосов
/ 05 января 2012

Рассматривается ли использование анализатора лямбда-выражений ?Вы должны быть в состоянии сделать что-то вроде:

[Verify("(Example e) => !e.Condition && e.Condition > 5")]
public void DoSomthing() 
{ ... }

и скомпилировать это позже, используя:

[Serializable]
public class MyAspectAttribute : OnMethodBoundaryAspect
{
    public override void OnExit(MethodExecutionArgs args)
    {
         ExprParser parser = new ExprParser();
         LambdaExpression lambda = parser.Parse(/* Verify string comes here */);
         bool isConditionMet = (bool) parser.Run(lambda, this);

         if (isConditionMet)
         { ... }
    }
}

Редактировать:

ДляПользовательские типы пользователей, вам необходимо предоставить пространство имен (и сборку, если тип находится в другой сборке, чем та, из которой вы вызываете анализатор):

ExprParser.Using.Add("PersonTypeNamespace");
ExprParser parser = new ExprParser();
LambdaExpression lambda = parser.Parse("(Person p) => p.Name");
var output = parser.Run(lambda, new Person { Name = "DragonFire" });

В секция загрузки есть Пользовательфайл справочника - содержит дополнительную информацию об использовании библиотеки.

1 голос
/ 05 января 2012

Создайте набор объектов, который реализует булеву логику.

  • Equals Comparator (принимает имя свойства и значение)
  • Greater Than Comparator (принимает имясвойство и значение)
  • И Компаратор (принимает два компаратора)
  • Логический оценщик (принимает имя логического свойства)
  • и т. д. и т. д.

Тогда пусть ваш атрибут примет один из этих объектов в качестве параметра

[Verify(new AndComparator(
    new BooleanEvaluator ("Condition"), 
    new GreaterThanComparator("ConditionValue", 5)))]
public void DoSomthing()
0 голосов
/ 05 января 2012

Вы уже знаете формулу, которую хотите использовать, поэтому просто передайте значения аспекту.

public class MyValidationAspect : OnMethodBoundaryAspect
{

public int Val1 { get; set; }
public int Val2 { get; set; }

public override void OnExit(MethodExecutionArgs args) 
    { 
        if (this.args.Arguments[0] > Val1) // If-statment from string, this is the problem... 
        { ... } 
    } 


}

[MyValidationAspect(Val1 = 5, Val2 = ...)]

Не пытайтесь быть настолько универсальными / динамическими, просто создайте набор конкретных аспектов и передайтезначения по мере необходимости.

Или вы можете использовать Rosylen (Компилятор как сервис).

0 голосов
/ 05 января 2012

Взгляните на кодовые контракты: http://research.microsoft.com/en-us/projects/contracts/ Если наличие пользовательских атрибутов не является обязательным, они могут быть именно тем, что вы ищете.

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

0 голосов
/ 05 января 2012

Одним из методов, который затруднит отладку, будет использование #define значений.

Например, в каком-то другом проекте вы можете объявить CONDITION_5 как:

#define CONDITION_5

Теперь в приведенном ниже примере класса видно DoSomething().

public class Example
{

#if CONDITION_5
  public void DoSomthing()
  { ... }
#endif
}

Если вам нужно, чтобы они были переменными для вашего кода, вы можете определить публичные переменные в отдельном классе для хранения ваших выборов:

public static class Global {
  public static bool Condition_5 = true;
}

Теперь в приведенном ниже примере класса DoSomething() всегда виден, но ничего не выполняется, если вы не установите значение.

public class Example
{

  public void DoSomthing()
  {
    if (Global.Condition_5)
    {
      // do stuff
    }
  }
}

Как и вы, я бы хотелчтобы увидеть более элегантный метод.

РЕДАКТИРОВАТЬ:

ОП изменил свой первоначальный вопрос, как я отмечаю в моем первом комментарии.Мой ответ соответствовал исходному вопросу, а не вопросу, на который был дан отмеченный ответ.

0 голосов
/ 05 января 2012

Почему бы не сделать что-то вроде этого:

public class Example
{
  public Boolean Condition {get; set;}
  public Double ConditionValue {get; set;}


  public void DoSomthing()
  { 
           Verify.That(!Condition && ConditionValue>5);
           ...
  }
}

Это почти такой же объем кода, он будет скомпилирован и проверен во время компиляции, вы сможете вносить изменения в конфигурацию, проверять приватные членыи делать сложные проверки.

...