首页 > 解决方案 > 指定类型的某些属性

问题描述

我正在尝试初始化对象以进行测试。初始化对象后,我想断言其基础类型是值类型的所有属性的值都不是属性类型的默认值,但有某些例外。我不想递归到子对象。为此,我有以下内容:

    public static void NoValueTypedPropertyHasDefaultValue<T>(this T t, params string[] exceptions)
        where T: class
    {
        foreach (var property in typeof(T).GetProperties())
        {
            var propertyType = property.PropertyType;
            if (propertyType.IsValueType && !exceptions.Any(x => x == property.Name))
            {
                var defaultValue = Activator.CreateInstance(propertyType);
                object actualValue = property.GetValue(t);
                Assert.NotEqual(defaultValue, actualValue);
            }
        }
    }

只要一切都有一个无参数的构造函数,它就可以工作。我对为异常传递字符串的方式并不感到兴奋。有没有更好的方法来做到这一点?

标签: c#reflectionproperties

解决方案


你在评论中提到:

我不高兴调用者可以只输入字符串,在这种情况下,它不会在属性重命名后继续存在。

使用System.Linq.Expressions可以为您提供编译时安全性,允许代码如下:

var inst = new MyType { MyInt1 = 1 }; // Set MyInt1 but not MyInt2
inst.NoValueTypedPropertyHasDefaultValue(x => x.MyInt2); // Ignore MyInt2 from validation

代码如下所示(H/T从 lambda 表达式检索属性名称):

public static class Extensions
{
    public static void NoValueTypedPropertyHasDefaultValue<T>(this T t, params Expression<Func<T,object>>[] propertiesToIgnore)
        where T : class
    {
        var propertyNamesToIgnore = propertiesToIgnore.Select(x => GetMemberInfo(x).Member.Name).ToArray();
        foreach (var property in typeof(T).GetProperties())
        {
            var propertyType = property.PropertyType;
            if (propertyType.IsValueType && !propertyNamesToIgnore.Contains(property.Name))
            {
                var defaultValue = Activator.CreateInstance(propertyType);
                object actualValue = property.GetValue(t);
                Assert.NotEqual(defaultValue, actualValue);
            }
        }
    }

    private static MemberExpression GetMemberInfo(Expression method)
    {
        LambdaExpression lambda = method as LambdaExpression;
        if (lambda == null)
            throw new ArgumentNullException("method");

        MemberExpression memberExpr = null;

        if (lambda.Body.NodeType == ExpressionType.Convert)
        {
            memberExpr =
                ((UnaryExpression)lambda.Body).Operand as MemberExpression;
        }
        else if (lambda.Body.NodeType == ExpressionType.MemberAccess)
        {
            memberExpr = lambda.Body as MemberExpression;
        }

        if (memberExpr == null)
            throw new ArgumentException("method");

        return memberExpr;
    }
}

现场示例:https ://dotnetfiddle.net/W9Q4gN (注意我已经Assert用一个例外替换了你的调用,只是为了测试)


但是,如果所有这些对您来说看起来有点疯狂/矫枉过正(在某种程度上,确实如此!!)请记住,您可以使用nameof原始方案。使用我的示例,您的代码将变为:

var inst = new MyType { MyInt1 = 1 }; // Set MyInt1 but not MyInt2
inst.NoValueTypedPropertyHasDefaultValue(nameof(inst.MyInt2)); // Ignore MyInt2 from validation

推荐阅读