C # Лямбда + Расширения + Свободно - Как бы я это сделал? - PullRequest
4 голосов
/ 22 августа 2009

Я хочу иметь возможность создавать классы «Преобразование», которые принимают данный объект, выполняют серию преобразований над ним (то есть изменяют значения свойств) и отслеживают выполненные преобразования. Выполненное преобразование зависит от свойств предоставленного объекта.

Я хочу иметь возможность применять правила преобразования (которые являются конечными и общими) в пределах данного класса преобразования с использованием интерфейса в свободном стиле.

На высоком уровне я понимаю, что, скорее всего, у меня будет ITransformer, ITransformationRule, ITransformationResult и несколько других объектов, чтобы это произошло.

Как бы я хотел, чтобы код работал при создании классов Transformation ...

public OfflinePersonToOnlineTransformation : TransformerBase<Person>
{
   public OfflinePersonToOnlineTransformation()
   {
        Transform(x => x.PersonClassification)
           .WhenCreatedBefore("1/1/2000")
           .ClassifyAs("Online");
   }
}

Я понимаю, что моя TransformerBase должна была бы реализовать метод "Transform", который принимает Func или Expression, и я понимаю, что он должен был бы хранить коллекцию ITransformationRules. Я также понимаю, что, скорее всего, буду использовать методы Extension для методов «WhenCreatedBefore» и «ClassifyAs».

Проблема в том, что я не могу понять, как заставить все это работать. Я посмотрел на исходный код для Fluent Validation .NET, так как он выполняет проверку таким образом, но сложность убивает меня. Я ищу учебник, который охватывает это, или кого-то, чтобы объяснить это довольно простым способом.

Заранее спасибо.

Ответы [ 3 ]

2 голосов
/ 23 августа 2009

Не совсем уверен, почему вы хотите пойти на все эти усилия, когда linq сделает большинство за вас:

IEnumerable<Person> somePeople; // from wherever
somePeople.Where(x => x.CreateDate < new DateTime(2000,1,1))
   .ForEach(x =>  x.PersonClassification = "Online");

Просто добавив ForEach из здесь , отметив, почему он не включен по умолчанию.

Если вы хотите сделать WhereCreatedBefore более привлекательным, просто добавьте следующее расширение:

static class PersonExtensions
{
    public static bool WhereCreatedBefore(this Person p,
        int year, int month, int day)
    {
         return p.CreateDate < new DateTime(year,month,day);
    }
}

, который полезен сам по себе и дает вам:

somePeople.Where(x => x.CreatedBefore(2000,1,1))
   .ForEach(x =>  x.PersonClassification = "Online");

Зачем ограничивать себя, когда просто расширяешь инструменты, которые дает linq, облегчает жизнь.

Если вы хотите объединить несколько побочных эффектов, просто измените ForEach следующим образом:

public static IEnumerable<T> Modify<T>(
    this IEnumerable<T> input, Action<T> action)
{
    foreach (var x in input)
    {
        action(x);
        yield return x;
    }
}

давая вам:

somePeople.Where(x => x.CreatedBefore(2000,1,1))
   .Modify(x =>  x.PersonClassification = "Online");
   .Modify(x =>  x.LastModifiedBy = Environment.UserName);

Или, если вы используете его интегрированную с языком часть:

(from p in somePeople where p.CreatedBefore(2000,1,1)) select p)
   .Modify(p =>  p.PersonClassification = "Online");
   .Modify(p =>  p.LastModifiedBy = Environment.UserName);

ЕСЛИ вы действительно * хотели, чтобы вы могли написать расширение ClassifyAs следующим образом:

public static IEnumerable<Person> ClassifyAs(
    this IEnumerable<Person> input, string classification)
{
    foreach (var p in input)
    {
        p. PersonClassification = classification;
        yield return p;
    }
}

давая вам оригинал:

(from p in input where p.CreatedBefore(2000,1,1)) select p).ClassifyAs("Online");

Который является одним вкладышем! без каких-либо причудливых структур или иерархий типов, просто некоторые полезные методы расширения. Linq обычно хорошо спроектирован, хорошо реализован, вездесущ и хорошо интегрирован в c #. Реализовать части запроса в нем было бы глупо и расточительно. Вам нужно добавить побочный эффект, вызывающий операции с ним. Это нормально (у вас есть изменяемые объекты, так что это вряд ли вызывает проблемы), просто добавьте эти операции. Просто заставляя их продолжать приносить свой вклад, вы сделаете ваш код свободным в стиле.

0 голосов
/ 22 августа 2009

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

    public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    class Calculator
    {
        List<int> values = new List<int>();

        public Calculator Add(int value)
        {
            values.Add(value);
            return this;
        }

        public int Count()
        {
            return values.Count;                
        }

        public int Sum()
        {
            return values.Sum();
        }

    }
    private void Form1_Load(object sender, EventArgs e)
    {
        //returns 3
        int sum =
            new Calculator()
            .Add(1)
            .Add(2)
            .Sum();
    }
}
0 голосов
/ 22 августа 2009

Я подумал; Это единственный псевдокод, но поможет ли это?

public interface IPerson {
  string PersonClassification { get; set; }
  DateTime CreateDate { get; set; }
 }

public class Person :  IPerson {
  public string PersonClassification { get; set; }
  public DateTime CreateDate { get; set; }
 }




public class TransformerBase<T> 
 where T : IPerson {

    T Person { get; set; }

    T Transform(Func<T, PersonClassification> transformer) {
        return transformer(person);
    }
}


public class  OfflinePersonToOnlineTransformation : TransformerBase<Person>
{
   public OfflinePersonToOnlineTransformation()
   {
        Transform(x => x.PersonClassification)
           .WhenCreatedBefore("1/1/2000")
           .ClassifyAs("Online");
   }
}

public static class Extensions {

    public static T WhenCreatedBefore<T>(this T person, string date) where T : IPerson{
        if(person == null || person.CreateDate > DateTime.Parse(date)) 
            return null
        return person;  
    }
    public static T Classify<T>(this T person, string classification)where T : IPerson{
        if(person != null) 
            person.PersonClassification = classification;
        return person;  
    }
}
...