【问题标题】:C# Lambda+Extensions+Fluent - How Would I Do This?C# Lambda+Extensions+Fluent - 我该怎么做?
【发布时间】:2009-08-22 08:21:21
【问题描述】:

我希望能够创建采用给定对象的“转换”类,对其执行一系列转换(即更改属性值)并跟踪执行的转换。所执行的转换将根据所提供对象的属性而有所不同。

我希望能够使用流畅的样式界面在给定的转换类中应用转换规则(它们是有限的和通用的)。

在高层次上,我知道我可能需要一个 ITransformer、一个 ITransformationRule 和 ITransformationResult 以及一些其他对象来实现这一点。

在创建转换类时我希望代码如何工作...

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

我了解我的 TransformerBase 需要实现采用 Func 或 Expression 的“Transform”方法,并且我了解它需要保留 ITransformationRules 的集合。我也明白我可能会为“WhenCreatedBefore”和“ClassifyAs”方法使用扩展方法。

问题是,我不知道如何让它全部工作。我已经查看了 Fluent Validation .NET 的源代码,因为它以这种方式进行验证,但复杂性让我感到厌烦。我正在寻找涵盖此内容的教程,或者以非常简单的方式将其拼写出来的人。

提前致谢。

【问题讨论】:

  • 请不要告诉我我不应该这样做......我已经承诺学习它......如果你不能帮助我这样做,请不要回答。
  • 如果您真的想尝试从表达式中提取属性,请参阅我对stackoverflow.com/questions/1061613/…的回答

标签: c# lambda expression-trees fluent-interface


【解决方案1】:

当 linq 为您完成大部分工作时,不太清楚为什么要进行所有这些工作:

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

只需添加来自here 的 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# 中。重新实现它的查询部分将是愚蠢和浪费的,你想要的是向它添加导致操作的副作用。这很好(你有可变对象,所以这几乎不会引起问题)只需添加这些操作。只是让他们继续产生他们的输入将使您的代码风格流利。

【讨论】:

  • 这对我来说是值得的,主要是因为我想将这种技术应用到一段将被大量使用的代码中。这样做的能力将极大地回报前期开发人员。
  • 我并不是建议不要添加有用的扩展方法,但是您提出的方案比我的预期方案更净呢?我的不那么冗长,更灵活(所有现有的 IEnumerable 扩展仍然可用)并且更惯用。性能明智,它应该是一个洗涤。当存在极其强大的查询机制时,您正试图重写查询机制。
【解决方案2】:

我有一个想法;它只是一个伪代码,但这有帮助吗?

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;  
    }
}

【讨论】:

  • PersonClassification 不是类型,不能用作泛型类型参数...
【解决方案3】:

退后一步,先写一个简单流畅的界面可能会有所帮助。您不需要泛型或多个类来实现一个。流畅的界面模式的主要好处是易于阅读代码。它是通过从方法中返回 this 以促进方法链接来完成的。这是一个基本的例子。我会从这里开始,然后回到你想要的结果。

    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();
    }
}

【讨论】:

  • 创建一个 Fluent 界面很容易,是的。以我要问的方式做这件事不是。一旦你开始绑定 lambdas 表达式树和扩展方法,事情就会变得更加复杂。
猜你喜欢
  • 1970-01-01
  • 2018-01-16
  • 1970-01-01
  • 2012-05-26
  • 1970-01-01
  • 2017-08-26
  • 2014-01-23
  • 2011-05-17
  • 1970-01-01
相关资源
最近更新 更多