【问题标题】:Listing object properties like the Visual Studio Immediate window列出对象属性,如 Visual Studio 即时窗口
【发布时间】:2010-12-21 21:48:46
【问题描述】:

我在会话中存储了一些类。我希望能够在跟踪查看器中查看我的类属性的值。默认情况下,我只有类型名称 MyNamespace.MyClass。我想知道我是否覆盖了 .ToString() 方法并使用反射来循环所有属性并构造一个这样的字符串......它可以解决问题,但只是想看看是否有任何已经存在的(特别是因为即时窗口具有此功能),它们的作用相同......即在跟踪中列出类属性值,而不仅仅是类的名称。

【问题讨论】:

    标签: c# reflection


    【解决方案1】:

    你可以试试这样的:

    static void Dump(object o, TextWriter output)
    {
        if (o == null)
        {
            output.WriteLine("null");
            return;
        }
    
        var properties =
            from prop in o.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
            where prop.CanRead
            && !prop.GetIndexParameters().Any() // exclude indexed properties to keep things simple
            select new
            {
                prop.Name,
                Value = prop.GetValue(o, null)
            };
    
        output.WriteLine(o.ToString());
        foreach (var prop in properties)
        {
            output.WriteLine(
                "\t{0}: {1}",
                prop.Name,
                (prop.Value ?? "null").ToString());
        }
    }
    

    当然,由于反射,效率不是很高……更好的解决方案是为每个特定类型动态生成并缓存转储器方法。


    编辑:这是一个改进的解决方案,它使用 Linq 表达式为每种类型生成专门的转储方法。稍微复杂一点;)

    static class Dumper
    {
        private readonly static Dictionary<Type, Action<object, TextWriter>> _dumpActions
            = new Dictionary<Type, Action<object, TextWriter>>();
    
        private static Action<object, TextWriter> CreateDumper(Type type)
        {
            MethodInfo writeLine1Obj = typeof(TextWriter).GetMethod("WriteLine", new[] { typeof(object) });
            MethodInfo writeLine1String2Obj = typeof(TextWriter).GetMethod("WriteLine", new[] { typeof(string), typeof(object), typeof(object) });
    
            ParameterExpression objParam = Expression.Parameter(typeof(object), "o");
            ParameterExpression outputParam = Expression.Parameter(typeof(TextWriter), "output");
            ParameterExpression objVariable = Expression.Variable(type, "o2");
            LabelTarget returnTarget = Expression.Label();
            List<Expression> bodyExpressions = new List<Expression>();
    
            bodyExpressions.Add(
                // o2 = (<type>)o
                Expression.Assign(objVariable, Expression.Convert(objParam, type)));
    
            bodyExpressions.Add(
                // output.WriteLine(o)
                Expression.Call(outputParam, writeLine1Obj, objParam));
    
            var properties =
                from prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
                where prop.CanRead
                && !prop.GetIndexParameters().Any() // exclude indexed properties to keep things simple
                select prop;
    
            foreach (var prop in properties)
            {
                bool isNullable =
                    !prop.PropertyType.IsValueType ||
                    prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>);
    
                // (object)o2.<property> (cast to object to be passed to WriteLine)
                Expression propValue =
                     Expression.Convert(
                         Expression.Property(objVariable, prop),
                         typeof(object));
    
                if (isNullable)
                {
                    // (<propertyValue> ?? "null")
                    propValue =
                        Expression.Coalesce(
                            propValue,
                            Expression.Constant("null", typeof(object)));
                }
    
                bodyExpressions.Add(
                    // output.WriteLine("\t{0}: {1}", "<propertyName>", <propertyValue>)
                    Expression.Call(
                        outputParam,
                        writeLine1String2Obj,
                        Expression.Constant("\t{0}: {1}", typeof(string)),
                        Expression.Constant(prop.Name, typeof(string)),
                        propValue));
            }
    
            bodyExpressions.Add(Expression.Label(returnTarget));
    
            Expression<Action<object, TextWriter>> dumperExpr =
                Expression.Lambda<Action<object, TextWriter>>(
                    Expression.Block(new[] { objVariable }, bodyExpressions),
                    objParam,
                    outputParam);
    
            return dumperExpr.Compile();
        }
    
        public static void Dump(object o, TextWriter output)
        {
            if (o == null)
            {
                output.WriteLine("null");
            }
    
            Type type = o.GetType();
            Action<object, TextWriter> dumpAction;
            if (!_dumpActions.TryGetValue(type, out dumpAction))
            {
                dumpAction = CreateDumper(type);
                _dumpActions[type] = dumpAction;
            }
            dumpAction(o, output);
        }
    }
    

    用法:

    Dumper.Dump(myObject, Console.Out);
    

    【讨论】:

    • Thomas,谢谢,这看起来是个不错的解决方案,但我仍在使用 .NET 3.5,因此 TragetLabel、Expression.Block() 等内容无法编译。
    【解决方案2】:

    这是我使用的代码。我发现它非常有用,几乎是即时的。该代码使用 Newtonsoft JSON 转换器。

        [System.Obsolete("ObjectDump should not be included in production code.")]
        public static void Dump(this object value)
        {
            try
            {
                System.Diagnostics.Trace.WriteLine(JsonConvert.SerializeObject(value, Formatting.Indented));
            }
            catch (Exception exception)
            {
                System.Diagnostics.Trace.WriteLine("Object could not be formatted. Does it include any interfaces? Exception Message: " + exception.Message);
            }
        }
    

    将此添加到公共库中,引用它并将其添加到 using 子句中。可以通过键入 YourObject.Dump() 在即时窗口中使用(与您在 linqpad 中所做的完全相同)。

    必须以不同方式处理包括接口在内的类,因为这只是一个 JSON 转换器。对于包含接口实现的类,我使用的一种解决方法是删除默认的空构造函数并使用接口的特定实例实现构造函数。

    我发现 JSON 是一种非常易于阅读的格式,并且认为这种小方法对于调试非常有用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多