Linq.Expression 实际应用案例

在开发过程中,经常需要处理一些复杂的逻辑,如属性更改通知、参数验证、模拟对象创建等。Linq.Expression 提供了一种强大的方式来简化这些操作。本文将通过五个实际案例,展示如何使用 Linq.Expression 来实现这些功能。

1. 实现INotifyPropertyChanged接口

在 WPF 或其他 MVVM 框架中,经常需要实现 INotifyPropertyChanged 接口来通知属性更改。传统的实现方式需要编写大量的模板代码,但通过使用 Linq.Expression,可以简化这个过程。

首先,定义一个基类 PropertyChangeBase,它实现了INotifyPropertyChanged接口,并提供了一个 SignalChanged 方法。

public class PropertyChangeBase : INotifyPropertyChanged { protected void SignalChanged<T>(Expression<Func<T>> exp) { if (exp.Body.NodeType == ExpressionType.MemberAccess) { var name = ((MemberExpression)exp.Body).Member.Name; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); } else { throw new Exception("Unexpected expression"); } } public event PropertyChangedEventHandler PropertyChanged; }

然后,可以通过继承这个基类,并调用 SignalChanged 方法来实现属性更改通知。

public class Customer : PropertyChangeBase { private string _customerName; public string CustomerName { get { return _customerName; } set { if (value != _customerName) { _customerName = value; SignalChanged(() => CustomerName); } } } }

这种方式的好处是,它支持 IntelliSense,并且对重构友好,可以轻松地更改属性名称而不需要修改 SignalChanged 方法。

2. 参数验证

在编写方法时,经常需要对参数进行验证。传统的验证方式需要硬编码参数名称,这在重构时会带来麻烦。通过使用 Linq.Expression,可以避免这个问题。

public void DoSomething(int arg1) { Contract.Expect(() => arg1).IsGreaterThan(0).IsLessThan(100); }

这种方式的好处是,它支持 IntelliSense,并且对重构友好,可以轻松地更改参数名称而不需要修改验证逻辑。

3. MoQ 模拟对象库

MoQ 是一个 .NET 库,用于创建模拟对象。它内部使用 Linq.Expression 来实现可读性强的语法。

mock.Setup(framework => framework.DownloadExists("2.0.0.0")).Returns(true).AtMostOnce();

这种方式的好处是,它支持 IntelliSense,并且可以轻松地创建模拟对象。

4. 通用交换函数

在 C# 中,可以使用 ref 关键字来交换两个变量的值。但是,如果想要交换对象的属性或数组的元素,就需要编写更复杂的代码。通过使用 Linq.Expression,可以编写一个通用的交换函数。

public class Swapper { public static void Swap<T>(Expression<Func<T>> left, Expression<Func<T>> right) { var lvalue = left.Compile()(); var rvalue = right.Compile()(); switch (left.Body.NodeType) { case ExpressionType.ArrayIndex: AssignTo(rvalue, left.Body as BinaryExpression); break; case ExpressionType.Call: AssignTo(rvalue, left.Body as MethodCallExpression); break; default: AssignTo(left, rvalue); break; } switch (right.Body.NodeType) { case ExpressionType.ArrayIndex: AssignTo(lvalue, right.Body as BinaryExpression); break; case ExpressionType.Call: AssignTo(lvalue, right.Body as MethodCallExpression); break; default: AssignTo(right, lvalue); break; } } private static void AssignTo<T>(T value, MethodCallExpression methodCall) { var setter = GetSetMethodInfo(methodCall.Method.DeclaringType, methodCall.Method.Name); Expression.Lambda<Action>( Expression.Call(methodCall.Object, setter, Join(methodCall.Arguments, Expression.Constant(value))) ).Compile()(); } private static Expression[] Join(ReadOnlyCollection<Expression> args, Expression exp) { List<Expression> exps = new List<Expression>(); exps.AddRange(args); exps.Add(exp); return exps.ToArray(); } private static MethodInfo GetSetMethodInfo(Type target, string name) { var setName = Regex.Replace(name, "get", new MatchEvaluator((m) => m.Value.StartsWith("g") ? "set" : "Set"), RegexOptions.IgnoreCase); var setter = target.GetMethod(setName); if (null == setter) { throw new Exception("can't find an expected method named:" + setName); } return setter; } private static void AssignTo<T>(Expression<Func<T>> left, T value) { Expression.Lambda<Func<T>>(Expression.Assign(left.Body, Expression.Constant(value))).Compile()(); } private static void AssignTo<T>(T value, BinaryExpression binaryExp) { Expression.Lambda<Func<T>>(Expression.Assign(Expression.ArrayAccess(binaryExp.Left, binaryExp.Right), Expression.Constant(value))).Compile()(); } }

这种方式的好处是,它支持 IntelliSense,并且可以轻松地交换对象的属性或数组的元素。

5.单元测试属性存在性

在开发过程中,经常需要对类的方法进行单元测试,以确保它们具有特定的属性。通过使用 Linq.Expression,可以编写简洁的测试代码。

var controller = new HomeController(); controller.ShouldHave(x => x.Index(), typeof(AuthorizeAttribute));
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485