Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/264.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 缓存编译的表达式委托_C#_Lambda_Expression Trees - Fatal编程技术网

C# 缓存编译的表达式委托

C# 缓存编译的表达式委托,c#,lambda,expression-trees,C#,Lambda,Expression Trees,背景:在整个项目中,我们经常使用System.ComponentModel.DataAnnotations.Validator对象以及属性来验证传递给API的属性。Validator对象需要传入属性名和值,这是公平的,但我们都有点被传递给这些属性名的[不断增加的]魔术字符串的数量弄晕了。这不仅会带来键入错误属性名称的风险,而且还会降低可维护性。如果其中一个属性再次重命名,那么这些属性的数量会过多 为了自动解析成员名,为了简单起见,我创建了这个helper方法,假设我只处理引用类型属性,因此p:c

背景:在整个项目中,我们经常使用System.ComponentModel.DataAnnotations.Validator对象以及属性来验证传递给API的属性。Validator对象需要传入属性名和值,这是公平的,但我们都有点被传递给这些属性名的[不断增加的]魔术字符串的数量弄晕了。这不仅会带来键入错误属性名称的风险,而且还会降低可维护性。如果其中一个属性再次重命名,那么这些属性的数量会过多

为了自动解析成员名,为了简单起见,我创建了这个helper方法,假设我只处理引用类型属性,因此p:class约束:

public static P ValidateProperty<T, P>(T obj, Expression<Func<T, P>> member, List<ValidationResult> results, Func<P, P> onValidCallback = null)
    where T : class
    where P : class
{
    var expr = member.Compile();
    var memberName = ((MemberExpression)member.Body).Member.Name;
    var value = expr(obj);

    bool isValid = Validator.TryValidateProperty(value, new ValidationContext(obj) { MemberName = memberName }, results);
    return isValid ? (onValidCallback != null ? onValidCallback(value) : value) : null;
}
这就像一个符咒,不涉及任何魔术字符串被传递

示例请求如下所示:

var value = ValidateProperty(myObject, x => x.PropertyFoo, errors, result => result.Trim());
public class Request
{
    public class C1
    {
        Property1;
        Property2;
        ...
    }
    public class C2
    {
        Property1;
        Property2;
        ...
    }
    public class C3
    {
        Property1;
        Property2;
        ...
    }
    ...
}
然而,我在这里关注的是member.Compile的性能影响。由于T、pc1、Property1、C1、Property2、C2、Property1等有许多不同的可能排列,我将无法缓存编译后的表达式并在下一次调用时执行它,除非T和P属于同一类型,这种情况很少发生

我可以通过将Expression成员更改为Expression成员来优化它,这样现在我只需要为每种类型的T(即C1、C2等)缓存一次表达式


我想知道是否有人对更好的方法有任何意见,或者我是在试图过度设计这个问题?在涉及反复传递魔法字符串的情况下,是否有一个共同的模式

在C 6 VS2015中,可以使用nameof。这个对ValidateProperty方法的调用涉及到一个额外的参数名property vs.property value,这引入了一点冗余,但解决了动态编译大量内容的潜在性能问题

var value = ValidateProperty(myObject, nameof(PropertyFoo), PropertyFoo, errors, result => result.Trim());

另一种选择:不要使用C 6特性或编译,只需使用反射即可。现在,您正在用编译性能下降来换取反射性能下降,而反射性能下降可能会更少

...
var property = (PropertyInfo) ((MemberExpression)member.Body).Member;
var propertyName = property.Name;
var value =  property.GetValue(obj, new object[0]);
...
你说得对,Expression.Compile确实有很大的性能开销。如果性能确实是一个问题,而另一个答案中提到的反射方法仍然与您有关,那么下面介绍如何实现编译委托缓存:

public static class ValidateUtils
{
    static readonly Dictionary<Type, Dictionary<string, Delegate>> typeMemberFuncCache = new Dictionary<Type, Dictionary<string, Delegate>>();

    public static P ValidateProperty<T, P>(this T obj, Expression<Func<T, P>> member, List<ValidationResult> results, Func<P, P> onValidCallback = null)
        where T : class
        where P : class
    {
        var memberInfo = ((MemberExpression)member.Body).Member;
        var memberName = memberInfo.Name;
        Func<T, P> memberFunc;
        lock (typeMemberFuncCache)
        {
            var type = typeof(T);
            Dictionary<string, Delegate> memberFuncCache;
            if (!typeMemberFuncCache.TryGetValue(type, out memberFuncCache))
                typeMemberFuncCache.Add(type, memberFuncCache = new Dictionary<string, Delegate>());
            Delegate entry;
            if (memberFuncCache.TryGetValue(memberName, out entry))
                memberFunc = (Func<T, P>)entry;
            else
                memberFuncCache.Add(memberName, memberFunc = member.Compile());
        }
        var value = memberFunc(obj);
        bool isValid = Validator.TryValidateProperty(value, new ValidationContext(obj) { MemberName = memberName }, results);
        return isValid ? (onValidCallback != null ? onValidCallback(value) : value) : null;
    }
}

顺便说一句,我将删除p:class约束,从而允许验证数值、日期等值,但无论如何

这会让你回到神奇的字符串,但是你考虑过新的C6 nameof操作符而不是od表达式吗?伙计,nameof正是我需要的,但不幸的是我们现在被困在C5上…不幸的是我们现在没有使用C6,但是nameof绝对是我需要的:/这看起来像是一种不想传递字符串的过激行为吗…?嗯,正如我所说的,nameof是最好的。除此之外,我不会称之为矫枉过正,这与dynamic在幕后所做的非常相似。当然,只有在您有性能问题时才使用它。反射方法可能是一种折衷方法,我本打算向您推荐它作为一种选择,但看到另一个答案是针对它的。