【问题标题】:Specify certain properties of a type指定类型的某些属性
【发布时间】:2021-01-09 23:45:26
【问题描述】:

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

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

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

【问题讨论】:

  • 定义:更好。听起来很固执。
  • 我不高兴调用者可以只输入字符串,在这种情况下,它无法在属性重命名后继续存在。
  • 我可能会将exceptions 重命名为propertiesToIngnore,异常对我来说意味着各种运行时错误异常。然后将它们从字符串更改为PropertyInfo 对象
  • 嗯,typeof(MyObject).GetProperty(nameof(MyObject.PropertyName))) 似乎是创建 PropertyInfo 对象的方法?不确定我喜欢那个。绝对喜欢 propertiesToIgnore。
  • 您提到:“前提是一切都有一个无参数的构造函数”。您无需担心 - 因为您已经在过滤值类型(propertyType.IsValueType,并且“不尝试递归到子对象”),Activator.CreateInstance 可以正常工作。但请注意,string 不是值类型,因此您将跳过任何字符串属性。

标签: c# reflection properties


【解决方案1】:

您在 cmets 中提到:

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

使用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 to Retrieving Property name from lambda expression):

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

【讨论】:

    猜你喜欢
    • 2021-07-06
    • 2021-08-28
    • 1970-01-01
    • 2017-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-02
    相关资源
    最近更新 更多