高级软件开发人员使用泛型和反射来节省编写数百行代码的工作。有一系列的技巧已经使用了多年,但不幸的是,它们并不广为人知。以下是其中的一些技巧。
这样做的原因很简单。如果你对属性名称进行硬编码,然后更改它们,你可能会在不知不觉中破坏它。使用此技巧,您可以安全地更改属性名称。
// Instead of
// var nameProp = "Name";
// you can use
var nameProp = PropertyName((Foo i) => i.Name);
public static string PropertyName<Entity, Prop>(
Expression<Func<Entity, Prop>> expression)
{
MemberExpression member = expression.Body as MemberExpression;
return member.Member.Name;
}
在对象之间映射公共属性是业务软件中的一项常见任务。通过使用泛型和反射,您可以在执行此任务时节省大量时间和代码。
下面是一个简化的映射器类示例
public class Mapper<TSource, TTarget>
where TSource : class
where TTarget : class
{
public void Map(TSource source, TTarget target)
{
var typeSource = typeof(TSource);
var properties = typeSource.GetProperties();
// loop each property
foreach (var sourceProperty in properties)
{
CopyValues(source, sourceProperty, target);
}
}
public void CopyValues(TSource source, PropertyInfo sourceProperty, TTarget target)
{
var typeTarget = typeof(TTarget);
var targetProperty = typeTarget.GetProperty(sourceProperty.Name);
if (targetProperty == null) return;
object value = sourceProperty.GetValue(source, null);
targetProperty.SetValue(target, value, null);
}
}
现在,无需手动映射所有常见属性,如下所示
var foo = new Foo();
var bar = new Bar();
bar.Name = foo.Name;
bar.Name1 = foo.Name1;
bar.Name2 = foo.Name2;
bar.Name3 = foo.Name3;
您只需按上面所示调用 mapper:
var mapper = new Mapper<Foo, Bar>();
mapper.Map(foo, bar);
信不信由你,即使方法被标记为私有,它仍然可以从其声明外部调用。我不确定这是否是一个安全漏洞,但您应该注意这一点,并考虑其他方法来保护您的方法。除此之外,您可以使用反射来访问和使用这些方法。
考虑这个带有私有 a 方法的 class
public class PrivateMethodClass
{
private void GetPrivateThings()
{
Console.WriteLine("This is private");
}
}
您可以通过这种方式调用 private 方法
var instance = new PrivateMethodClass();
var type = typeof(PrivateMethodClass);
var privateMethod = type.GetMethod("GetPrivateThings", BindingFlags.NonPublic | BindingFlags.Instance);
privateMethod.Invoke(instance, null);
是的,我们喜欢 LINQ,而且,您可以轻松地扩展它。例如,假设您需要一个方法,该方法从两个特定日期之间的列表中返回元素。
您可以执行以下操作
public static class CollectionExtensions
{
public static IEnumerable<T> Between<T>(
this IEnumerable<T> source,
Func<T, DateTime> getDate,
DateTime from,
DateTime to)
{
return source.Where(entity =>
getDate(entity) >= from &&
getDate(entity) <= to);
}
}
并像这样使用它
var dates = new List<Foo>() {
new Foo() { Date = new DateTime(2024, 1, 1)},
new Foo() { Date = new DateTime(2024, 1, 2)}
};
var from = new DateTime(2024, 1, 1);
var to = new DateTime(2024, 1, 1);
var filtered = dates.Between(x => x.Date, from, to);
假设我们想过滤一个具有特定条件的列表
var fooList = new List<Foo>() {
// elements
};
var res = fooList
.Where(i => i.Condition1 == "value1"
&& i.Condition2 == "value2"
&& i.Condition3 == "value2"
&& i.Condition4 == "value4"
);
这样做的问题在于,过滤条件 Expression 必须在编译时编写。在运行时创建条件的另一种方法是使用反射构建 Expression。
构建表达式的常用方法是如下所示的类
public class ExpressionBuilder<T>
{
// creates expresion entity => entity.propertyName == value
public Expression CreatePropertyExpression(
Expression entityParameter,
string propertyName, object? value)
{
var property = typeof(T).GetProperty(propertyName);
var propertyAccess = Expression.MakeMemberAccess(entityParameter, property);
var valueExpression = Expression.Constant(value, property.PropertyType);
var equalsExpression = Expression.Equal(propertyAccess, valueExpression);
return equalsExpression;
}
// creates expresion entity => entity.propertyName1 == value1
// && entity.propertyName1 == value1
// && entity.propertyNameN == valueN
public Func<T, bool> ComplexExpression(
List<Tuple<string, object>> conditions
)
{
Expression expression = null;
var parameter = Expression.Parameter(typeof(T), "entity");
foreach (var condition in conditions)
{
var currentExpression = CreatePropertyExpression(
parameter,
condition.Item1,
condition.Item2);
if (expression == null)
{
expression = currentExpression;
continue;
}
expression = Expression.AndAlso(expression, currentExpression);
}
var lambda = Expression.Lambda<Func<T, bool>>(expression, parameter);
return lambda.Compile();
}
}
你可以像这样使用它
var builder = new ExpressionBuilder<Foo>();
// this can be create it at runtime
var condition = new List<Tuple<string, object>>() {
new Tuple<string, object>("Condition1", "Value1"),
new Tuple<string, object>("Condition2", "Value2"),
// etc
};
var expression = builder.ComplexExpression(condition);
var filtered = list.Where(expression).ToList();