Лямбда-выражения C# или делегаты как свойства или аргументы

Это зависит от изменений, которые вы наблюдаете. В современных компьютерах все разработано для статистической оптимизации среднего времени работы. Но многие архитектурные особенности основаны на статистике и в зависимости от начальных условий могут привести к очень разному времени выполнения. Это включает в основном кеши и предсказатели ветвлений. Нормально иметь вариации около 20-30% во время выполнения. И мы проводили эксперименты на системах без ОС и с таким же поведением.

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

#include <stdio.h>
#include <stdlib.h>
#define N 1000000
#define TYPE int
#define ZERO 0

static unsigned long long start_timer() ;
static unsigned long long stop_timer() ;
static double dtime(long long debut, long long fin);


#ifdef __i386__
#  define RDTSC_DIRTY "%eax", "%ebx", "%ecx", "%edx"
#elif __x86_64__
#  define RDTSC_DIRTY "%rax", "%rbx", "%rcx", "%rdx"
#else
# error unknown platform
#endif


static inline unsigned long long start_timer() 
{ 
  unsigned int hi = 0, lo = 0; 

  asm volatile("cpuid\n\t"
           "rdtscp\n\t"
           "mov %%edx, %0\n\t"
           "mov %%eax, %1\n\t" 
           : "=r" (hi), "=r" (lo)
           :: RDTSC_DIRTY);      
  unsigned long long that =   (unsigned long long)((lo) |
                           (unsigned long long)(hi)<<32);

  return that; 
} 

static inline unsigned long long stop_timer() 
{ 
  unsigned int hi = 0, lo = 0; 

  asm volatile("rdtscp\n\t"    
           "mov %%edx, %0\n\t" 
           "mov %%eax, %1\n\t" 
           "cpuid\n\t"     
           : "=r" (hi), "=r" (lo)
           :: RDTSC_DIRTY);      
  unsigned long long that =   (unsigned long long)((lo) |
                           (unsigned long long)(hi)<<32);

  return that; 
} 

static inline double dtime(long long start, long long end)
{
  return (double) (end - start) ;
}


TYPE BF[N] ;

long long start, end;
double benchtime;


void zero(){
  int i, j, m ;
  start=start_timer();
  for (i=0;i<N;i++)
    BF[i]=ZERO;
  benchtime=dtime(start, stop_timer());

  printf ("%g\n", benchtime);
}

void randtest(){
  int i, j, m ;
  srandom(100);
  for (i=0;i<N;i++)
    BF[i]=random();
  int count=0;
  start=start_timer();
  for (i=0;i<N;i++){
    if (BF[i]>RAND_MAX/2)
      count++;
  }
  benchtime=dtime(start, stop_timer());

  printf ("%g\n", benchtime);
}



void main()
{
  #ifdef ZEROTEST
  zero();
  #else
  randtest();
  #endif
} 

Вот результаты:

am @ Mandel: ~ / tmp / d $ cc -DZEROTEST time.c; для меня в 1 2 3 4 5 6 7 8 9 10; делать ./a.out; сделано

1.09084e + 07 1.14298e + 07 1.07197e + 07 1.26519e + 07 1.32742e + 07 1.37184e + 07 1.54689e + 07 1.36335e + 07 1.20818e + 07 1.12298e + 07

am @ Mandel: ~ / tmp / d $ cc -DZEROTEST -O time.c; для меня в 1 2 3 4 5 6 7 8 9 10; делать ./a.out; сделано

4.30112e + 06 4.37242e + 06 4.28102e + 06 4.51831e + 06 4.45952e + 06 5.77813e + 06 6.33686e + 06 5.44415e + 06 5.67434e + 06 5.90118e + 06

am @ Mandel: ~ / tmp / d $ cc time.c; для меня в 1 2 3 4 5 6 7 8 9 10; делать ./a.out; сделано

2.4763e + 07 2.77489e + 07 2.78568e + 07 3.3762e + 07 3.56298e + 07 3.66709e + 07 3.22833e + 07 2.68651e + 07 2.88412e + 07 2.92287e + 07

am @ Mandel: ~ / tmp / d $ cc -O time.c; для меня в 1 2 3 4 5 6 7 8 9 10; делать ./a.out; сделано

1.00543e + 06 1.15819e + 06 1.00544e + 06 2.74409e + 06 1.17561e + 06 1.40751e + 06 2.41898e + 06 1.65623e + 06 2.19502e + 06 1.59414e + 06

Как вы можете видеть, как правило, изменение времени составляет не менее 30%, но когда речь идет о предсказателях переходов, он может быть намного больше. И это происходит либо в оптимизированном, либо в неоптимизированном коде.

Вы не можете считать, что выполнение является чем-то детерминированным в современных архитектурах. Существуют вариации, связанные с выбором архитектурного дизайна, которые по крайней мере так же важны, как и эффекты ОС. И это является большой трудностью, когда речь идет о реальном времени.

7
задан Vertexwahn 4 March 2016 в 16:13
поделиться

5 ответов

Действительно, то, что Вы хотите использовать, Func<T,bool> где T является типом объекта, Вы хотите проверить. Затем Вы сделали бы что-то вроде этого

validator.AddValidation(item => (item.HasEnoughInformation() || item.IsEmpty());

Вы могли сохранить их в a List<Func<T,bool>>.

6
ответ дан 6 December 2019 в 21:21
поделиться
class ValidationRule {
    public delegate bool Validator();

    private Validator _v;

    public ValidationRule(Validator v) { _v = v; }

    public Validator Validator {
        get { return _v; }
        set { _v = value; }
    }

    public bool IsValid { get { return _v(); } }
}

var alwaysPasses = new ValidationRule(() => true);
var alwaysFails = new ValidationRule(() => false);

var textBoxHasText = new ValidationRule(() => textBox1.Text.Length > 0);

Это должно запустить Вас. Но, действительно, наследование является намного более соответствующим здесь. Проблема состоит просто в том что Validator не имеет доступа ни к какому состоянию, которое он не закрывает, это означает, что не является столь допускающим повторное использование, как говорят ValidationRules это содержит их собственное состояние. Сравните следующий класс с предыдущим определением textBoxHasText.

interface IValidationRule {
    bool IsValid { get; }
}

class BoxHasText : IValidationRule {
    TextBox _c;

    public BoxHasText(TextBox c) { _c = c; }

    public bool IsValid {
        get { return _c.Text.Length > 0; }
    }
}
3
ответ дан 6 December 2019 в 21:21
поделиться

Ну, просто, если у Вас есть класс Объекта, и Вы хотите использовать лямбда-выражения на том Объекте, чтобы определить, допустимо ли что-то (возврат булевской переменной), Вы могли бы использовать Func.

Так, учитывая Объект:

 class Entity
 {
      public string MyProperty { get; set; }
 }

Вы могли определить класс ValidationRule для того как это:

 class ValidationRule<T> where T : Entity
 {
      private Func<T, bool> _rule;

      public ValidationRule(Func<T, bool> rule)
      {
           _rule = rule;
      }

      public bool IsValid(T entity)
      {
           return _rule(entity);
      }
 }

Затем Вы могли использовать его как это:

 var myEntity = new Entity() { MyProperty = "Hello World" };
 var rule = new ValidationRule<Entity>(entity => entity.MyProperty == "Hello World");

 var valid = rule.IsValid(myEntity);

Конечно, это - всего одно возможное решение.

Если Вы удаляете универсальное ограничение выше ("где T: Объект"), Вы могли сделать это универсальным механизмом правил, который мог использоваться с любым ПОСТЕПЕННО. Вы не должны были бы получать класс для каждого типа использования, в котором Вы нуждаетесь. Таким образом, если бы я хотел использовать этот тот же класс на TextBox, то я мог бы использовать следующее (после удаления универсального ограничения):

 var rule = new ValidationRule<TextBox>(tb => tb.Text.Length > 0);
 rule.IsValid(myTextBox);

Это довольно гибко этот путь. Используя лямбда-выражения и дженерики вместе очень мощно. Вместо того, чтобы принять Func или Action, Вы могли принять Выражение> или Выражение> и иметь прямой доступ к специальному дереву для автоматического исследования вещей как название метода или свойства, какое выражение это и т.д. И люди, использующие Ваш класс, не должны были бы изменять одну строку кода.

2
ответ дан 6 December 2019 в 21:21
поделиться

Синтаксис определения правила хотел бы эту работу за Вас?

  public static void Valid(Address address, IScope scope)
  {
    scope.Validate(() => address.Street1, StringIs.Limited(10, 256));
    scope.Validate(() => address.Street2, StringIs.Limited(256));
    scope.Validate(() => address.Country, Is.NotDefault);
    scope.Validate(() => address.Zip, StringIs.Limited(10));

    switch (address.Country)
    {
      case Country.USA:
        scope.Validate(() => address.Zip, StringIs.Limited(5, 10));
        break;
      case Country.France:
        break;
      case Country.Russia:
        scope.Validate(() => address.Zip, StringIs.Limited(6, 6));
        break;
      default:
        scope.Validate(() => address.Zip, StringIs.Limited(1, 64));
        break;
    }

Проверьте DDD и Правило управляемая Проверка UI в.NET для получения дополнительной информации

0
ответ дан 6 December 2019 в 21:21
поделиться

что-то как:

class ValidationRule
{
    private Func<bool> validation;

    public ValidationRule(Func<bool> validation)
    {
        this.validation = validation;
    }
    public bool IsValid()
    {
        return validation();
    }
}

был бы больше стиля C# 3, но компилируется в тот же код как @Frank ответ Krueger. Это - то, что Вы попросили, но не чувствуете себя хорошо. Существует ли серьезное основание, почему объект не может быть расширен для выполнения проверки?

1
ответ дан 6 December 2019 в 21:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: