Iterate model inside model and detect changes - c#

I'm working on an Angular (front-end) C# (back-end) application. This application uses Entity Framework code first.
So I want to evaluate two models to compare two models and look for their differences. After some investigation, I found that it can be achieved using Reflection. So I have the following code:
public static List<Variance> Compare<T>(this T val1, T val2)
{
var variances = new List<Variance>();
var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var property in properties)
{
var v = new Variance
{
PropertyName = property.Name,
valA = property.GetValue(val1),
valB = property.GetValue(val2)
};
if (v.valA == null && v.valB == null)
{
continue;
}
if (
(v.valA == null && v.valB != null)
||
(v.valA != null && v.valB == null)
)
{
if(v.valA != null)
{
if (v.valA.ToString() == "0" && v.valB == null)
{
continue;
}
}
if(v.valA == null && v.valB != null)
{
continue;
}
variances.Add(v);
continue;
}
if (!v.valA.Equals(v.valB))
{
variances.Add(v);
}
}
return variances;
}
}
public class Variance
{
public string PropertyName { get; set; }
public object valA { get; set; }
public object valB { get; set; }
}
It is working, but only for root model and not their models inside the model. I.E, I'm comparing this model:
public partial class Profile : BaseAuditModel
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public virtual ICollection<EmploymentHistory> EmploymentHistories { get; set; }
}
The EmploymentHistories detects it has differences, but in reality, they do not have any difference; I think it is marked as a difference because the method does not know what is inside it, so my question is. How can I detect if it is a collection? and when it is a collection iterate and detect the changes as the root one

If you're concerned with child objects ideally defined within the same assembly then you can try something like this to detect those references and use a recursive call:
if (property.PropertyType.IsClass
&& property.PropertyType.Assembly.FullName == typeof(T).Assembly.FullName)
{
var subVariances = Compare(property.GetValue(val1), property.GetValue(val2));
if(subVariances.Any())
variances.AddRange(subVariances);
continue;
}
I cannot imagine this would be particularly efficient. A better approach might be to incorporate a dirty laundry list of changes if you want to do something like tracking changes. If you're doing a straight comparison of two unrelated objects then rather to jumping to a field-by-field inspection, a first pass might involve serializing both object graphs to JSON and doing a string comparison to determine if they are identical before committing to a field-by-field check.

Related

How to determine if property is a nullable reference type? [duplicate]

C# 8.0 introduces nullable reference types. Here's a simple class with a nullable property:
public class Foo
{
public String? Bar { get; set; }
}
Is there a way to check a class property uses a nullable reference type via reflection?
In .NET 6, the NullabilityInfoContext APIs were added to handle this. See this answer.
Prior to this, you need to read the attributes yourself. This appears to work, at least on the types I've tested it with.
public static bool IsNullable(PropertyInfo property) =>
IsNullableHelper(property.PropertyType, property.DeclaringType, property.CustomAttributes);
public static bool IsNullable(FieldInfo field) =>
IsNullableHelper(field.FieldType, field.DeclaringType, field.CustomAttributes);
public static bool IsNullable(ParameterInfo parameter) =>
IsNullableHelper(parameter.ParameterType, parameter.Member, parameter.CustomAttributes);
private static bool IsNullableHelper(Type memberType, MemberInfo? declaringType, IEnumerable<CustomAttributeData> customAttributes)
{
if (memberType.IsValueType)
return Nullable.GetUnderlyingType(memberType) != null;
var nullable = customAttributes
.FirstOrDefault(x => x.AttributeType.FullName == "System.Runtime.CompilerServices.NullableAttribute");
if (nullable != null && nullable.ConstructorArguments.Count == 1)
{
var attributeArgument = nullable.ConstructorArguments[0];
if (attributeArgument.ArgumentType == typeof(byte[]))
{
var args = (ReadOnlyCollection<CustomAttributeTypedArgument>)attributeArgument.Value!;
if (args.Count > 0 && args[0].ArgumentType == typeof(byte))
{
return (byte)args[0].Value! == 2;
}
}
else if (attributeArgument.ArgumentType == typeof(byte))
{
return (byte)attributeArgument.Value! == 2;
}
}
for (var type = declaringType; type != null; type = type.DeclaringType)
{
var context = type.CustomAttributes
.FirstOrDefault(x => x.AttributeType.FullName == "System.Runtime.CompilerServices.NullableContextAttribute");
if (context != null &&
context.ConstructorArguments.Count == 1 &&
context.ConstructorArguments[0].ArgumentType == typeof(byte))
{
return (byte)context.ConstructorArguments[0].Value! == 2;
}
}
// Couldn't find a suitable attribute
return false;
}
See this document for details.
The general gist is that either the property itself can have a [Nullable] attribute on it, or if it doesn't the enclosing type might have [NullableContext] attribute. We first look for [Nullable], then if we don't find it we look for [NullableContext] on the enclosing type.
The compiler might embed the attributes into the assembly, and since we might be looking at a type from a different assembly, we need to do a reflection-only load.
[Nullable] might be instantiated with an array, if the property is generic. In this case, the first element represents the actual property (and further elements represent generic arguments). [NullableContext] is always instantiated with a single byte.
A value of 2 means "nullable". 1 means "not nullable", and 0 means "oblivious".
.NET 6 Preview 7 adds reflection APIs to get nullability info.
Libraries: Reflection APIs for nullability information
Obviously, this only helps folks targeting .NET 6+.
Getting top-level nullability information
Imagine you’re implementing a serializer. Using these new APIs the serializer can check whether a given property can be set to null:
private NullabilityInfoContext _nullabilityContext = new NullabilityInfoContext();
private void DeserializePropertyValue(PropertyInfo p, object instance, object? value)
{
if (value is null)
{
var nullabilityInfo = _nullabilityContext.Create(p);
if (nullabilityInfo.WriteState is not NullabilityState.Nullable)
{
throw new MySerializerException($"Property '{p.GetType().Name}.{p.Name}'' cannot be set to null.");
}
}
p.SetValue(instance, value);
}
I wrote a library to do reflection of NRT types - internally it looks at the generated attributes and gives you a simple API:
https://github.com/RicoSuter/Namotion.Reflection
Late answer.
This is what I ended up using thanks to Bill Menees:
static bool IsMarkedAsNullable(PropertyInfo p)
{
return new NullabilityInfoContext().Create(p).WriteState is NullabilityState.Nullable;
}
// Tests:
class Foo
{
public int Int1 { get; set; }
public int? Int2 { get; set; } = null;
public string Str1 { get; set; } = "";
public string? Str2 { get; set; } = null;
public List<Foo> Lst1 { get; set; } = new();
public List<Foo>? Lst2 { get; set; } = null;
public Dictionary<int, object> Dic1 { get; set; } = new();
public Dictionary<int, object>? Dic2 { get; set; } = null;
}
....
var props = typeof(Foo).GetProperties();
foreach(var prop in props)
{
Console.WriteLine($"Prop:{prop.Name} IsNullable:{IsMarkedAsNullable(prop)}");
}
// outputs:
Prop:Int1 IsNullable:False
Prop:Int2 IsNullable:True
Prop:Str1 IsNullable:False
Prop:Str2 IsNullable:True
Prop:Lst1 IsNullable:False
Prop:Lst2 IsNullable:True
Prop:Dic1 IsNullable:False
Prop:Dic2 IsNullable:True
A great answer there by #rico-suter !
The following is for those who just want a quick cut-and-paste solution until the real McCoy is available (see the proposal https://github.com/dotnet/runtime/issues/29723 ).
I put this together based on #canton7's post above plus a short look at the ideas in #rico-suter's code. The change from the #canton7's code is just abstracting the list of attribute sources and including a few new ones.
private static bool IsAttributedAsNonNullable(this PropertyInfo propertyInfo)
{
return IsAttributedAsNonNullable(
new dynamic?[] { propertyInfo },
new dynamic?[] { propertyInfo.DeclaringType, propertyInfo.DeclaringType?.DeclaringType, propertyInfo.DeclaringType?.GetTypeInfo() }
);
}
private static bool IsAttributedAsNonNullable(this ParameterInfo parameterInfo)
{
return IsAttributedAsNonNullable(
new dynamic?[] { parameterInfo },
new dynamic?[] { parameterInfo.Member, parameterInfo.Member.DeclaringType, parameterInfo.Member.DeclaringType?.DeclaringType, parameterInfo.Member.DeclaringType?.GetTypeInfo()
);
}
private static bool IsAttributedAsNonNullable( dynamic?[] nullableAttributeSources, dynamic?[] nullableContextAttributeSources)
{
foreach (dynamic? nullableAttributeSource in nullableAttributeSources) {
if (nullableAttributeSource == null) { continue; }
CustomAttributeData? nullableAttribute = ((IEnumerable<CustomAttributeData>)nullableAttributeSource.CustomAttributes).FirstOrDefault(x => x.AttributeType.FullName == "System.Runtime.CompilerServices.NullableAttribute");
if (nullableAttribute != null && nullableAttribute.ConstructorArguments.Count == 1) {
CustomAttributeTypedArgument attributeArgument = nullableAttribute.ConstructorArguments[0];
if (attributeArgument.ArgumentType == typeof(byte[])) {
var args = (ReadOnlyCollection<CustomAttributeTypedArgument>)(attributeArgument.Value ?? throw new NullReferenceException("Logic error!"));
if (args.Count > 0 && args[0].ArgumentType == typeof(byte)) {
byte value = (byte)(args[0].Value ?? throw new NullabilityLogicException());
return value == 1; // 0 = oblivious, 1 = nonnullable, 2 = nullable
}
} else if (attributeArgument.ArgumentType == typeof(byte)) {
byte value = (byte)(attributeArgument.Value ?? throw new NullReferenceException("Logic error!"));
return value == 1; // 0 = oblivious, 1 = nonnullable, 2 = nullable
} else {
throw new InvalidOperationException($"Unrecognized argument type for NullableAttribute.");
}
}
}
foreach (dynamic? nullableContextAttributeSource in nullableContextAttributeSources) {
if (nullableContextAttributeSource == null) { continue; }
CustomAttributeData? nullableContextAttribute = ((IEnumerable<CustomAttributeData>)nullableContextAttributeSource.CustomAttributes).FirstOrDefault(x => x.AttributeType.FullName == "System.Runtime.CompilerServices.NullableContextAttribute");
if (nullableContextAttribute != null && nullableContextAttribute.ConstructorArguments.Count == 1) {
CustomAttributeTypedArgument attributeArgument = nullableContextAttribute.ConstructorArguments[0];
if (attributeArgument.ArgumentType == typeof(byte)) {
byte value = (byte)(nullableContextAttribute.ConstructorArguments[0].Value ?? throw new NullabilityLogicException());
return value == 1;
} else {
throw new InvalidOperationException($"Unrecognized argument type for NullableContextAttribute.");
}
}
}
return false;
}
It's only the string? which gets a bit tricky. The rest of the nullable types are pretty straightforward to find out. For strings I used the following method, which you need to pass in a PropertyInfo object taken via reflection.
private bool IsNullable(PropertyInfo prop)
{
return prop.CustomAttributes.Any(x => x.AttributeType.Name == "NullableAttribute");
}

C# Loop/Iterate through object to get property values with complex property types

I am trying to find a way to loop through and iterate through an object to get all of it's properties (their name and their value) of an object. I can successfully iterate through the simple properties (such as strings, int, etc.., but when it has a property that contains properties - that is where the problem is...
[ Working for Simple string/int/bool properties ], but I need something that will work with nested / complex property types.
foreach (PropertyInfo spotProperties in spot.GetType().GetProperties())
{
// Simple property type (string, int, etc...) add the property and its value to the node.
var attributeName = spotProperties.Name;
resultElement.Add(new XElement(attributeName, spotProperties.GetValue(spot, null)));
}
Sample code of what I am trying to accomplish, but could not get to work
// Unable to get to work loop through complex property types.
foreach (PropertyInfo spotProperties in spot.GetType().GetProperties())
{
if (--spotProperties is complex type then --)
{
// The item is a complex data type, and needs to have it's properties iterated and added to the node.
foreach (PropertyInfo childSpotProperty in spotProperties.GetValue(spot, null).GetType().GetProperties())
{
var attributeName = ((DisplayNameAttribute)childSpotProperty.GetCustomAttributes(typeof(DisplayNameAttribute), false).FirstOrDefault() as DisplayNameAttribute)?.DisplayName ?? childSpotProperty.Name;
//resultElement.Add(new XElement(attributeName, childSpotProperty.GetValue(childSpotProperty, null)));
}
}
else
{
// Simple property type (string, int, etc...) add the property and its value to the node.
var attributeName = spotProperties.Name;
resultElement.Add(new XElement(attributeName, spotProperties.GetValue(spot, null
}
}
Please let me know if anyone has any idea. Thanks, I appreciate any feed back.
You can refactor this to your liking but it should get the basic job done. It uses some recursion to move through all of the properties in the complex objects. It also handles properties that are Enumerable.
public class PropertyInformation
{
public string Name { get; set; }
public object Value { get; set; }
}
public static List<PropertyInformation> ObjectPropertyInformation(object obj)
{
var propertyInformations = new List<PropertyInformation>();
foreach (var property in obj.GetType().GetProperties())
{
//for value types
if (property.PropertyType.IsPrimitive || property.PropertyType.IsValueType || property.PropertyType == typeof(string))
{
propertyInformations.Add(new PropertyInformation { Name = property.Name, Value = property.GetValue(obj) });
}
//for complex types
else if (property.PropertyType.IsClass && !typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
{
propertyInformations.AddRange(ObjectPropertyInformation(property.GetValue(obj)));
}
//for Enumerables
else
{
var enumerablePropObj1 = property.GetValue(obj) as IEnumerable;
if (enumerablePropObj1 == null) continue;
var objList = enumerablePropObj1.GetEnumerator();
while (objList.MoveNext())
{
objList.MoveNext();
ObjectPropertyInformation(objList.Current);
}
}
}
return propertyInformations;
}
This works but it does have a bug.
The fix is shown below:
//for Enumerables
else
{
var enumerablePropObj1 = property.GetValue(obj) as IEnumerable;
if (enumerablePropObj1 == null) continue;
var objList = enumerablePropObj1.GetEnumerator();
while (objList.MoveNext())
{
== if(objList.Current != null)
== {
== propertyInformations.AddRange(ObjectPropertyInformation(objList.Current));
== }
}

Is it possible to make use of a list from an object by its object type?

I am trying to delete items from different lists og objects.
I have the below classes, in my case I will be given the the object name of the list and then I will be required to delete items from that list. Is it possible to access a specific list only by its object type?
As an example I will be given "TestSubcolleciton" and then i will have to access Subcollecitons list in order to delete some records.
private class TestClassWithSubcollection : BaseObject
{
public List<TestSubcolleciton> Subcollecitons { get; set; }
public List<TestSubcollecitonSecond> SubcollecitonSeconds { get; set; }
}
protected class TestSubcolleciton
{
protected int Id { get; set; }
}
protected class TestSubcollecitonSecond
{
protected int Id { get; set; }
}
This can be done using reflection, though this is probably a bad idea.
public static IList GetListByItemType(object instance, Type listItemType)
{
if(instance == null) throw new ArgumentNullException("instance");
if(listItemType == null) throw new ArgumentNullException("listItemType");
Type genericListType = typeof(List<>).MakeGenericType(listItemType);
PropertyInfo property = instance.GetType().GetProperties().FirstOrDefault(p => p.PropertyType == genericListType);
if(property != null)
return (IList)property.GetValue(instance);
return null;
}
This either returns null or the first reference of a List found.
You can then use it like this:
TestClassWithSubcollection instance = ...
IList list = GetListByItemType(instance, typeof(TestSubcollecitonSecond));
if(list != null)
{
// ...
}
If you need to get it by "Type name" of the list Item then do it like this:
public static IList GetListByItemType(object instance, string listItemTypeName)
{
if(instance == null) throw new ArgumentNullException("instance");
if(listItemTypeName== null) throw new ArgumentNullException("listItemTypeName");
PropertyInfo property = instance.GetType().GetProperties().FirstOrDefault(p => p.PropertyType.IsGenericType && p.PropertyType.GetGenericArguments()[0].Name== listItemTypeName);
if(property != null)
return (IList)property.GetValue(instance);
return null;
}
This is then used like this:
IList list = GetListByItemType(instance, "TestSubcollecitonSecond");
if(list != null)
{
// ...
}

VS2010 Code Analysis and CA1800 : Microsoft Performance

I have the code below which I know isn't optimal. I ran the Code Analysis and it gave me the warning
CA1800 : Microsoft.Performance : 'customField', a variable, is cast to type 'DateCustomFieldRef' multiple times in method 'Customer.CustomerToUpdat(SearchResult)'. Cache the result of the 'as' operator or direct cast in order to eliminate the redundant castclass instruction.
and I really don't understand what to do.
CustomFieldRef[] customFields = customer.customFieldList;
for (int f = 3; f < customFields.Length; f++)
{
CustomFieldRef customField = customFields[f];
if (customField is DateCustomFieldRef)
{
DateCustomFieldRef dateField = (DateCustomFieldRef)customField;
if (dateField.internalId != null && dateField.internalId == "created_date")
{
createdDate = dateField.value.ToString();
}
}
if (customField is StringCustomFieldRef)
{
StringCustomFieldRef tradingNameField = (StringCustomFieldRef)customField;
if (businessNameField.internalId != null && businessNameField.internalId == "business_name")
{
businessName = businessNameField.value;
}
}
}
}
Could someone please give me a code example or explain further what it really means?
Thanks in advance.
The problem is in code like:
if (customField is DateCustomFieldRef)
{
DateCustomFieldRef dateField = (DateCustomFieldRef)customField;
These are multiple casts.
Better:
DateCustomFieldRef fieldasDate = customField as DateCustomFieldFRef
if (fieldasDate != null)
{
blablabla using fieldasdate
This avoids the multiple casts.
It means that you're casting (which can be costly) the customField variable multiple times, and that you'd be better of by casting only once.
You can use the as operator to achieve that, since the as operator performs the cast and returns an instance of the desired type, or NULL if the object could not be casted to the desired type.
Like this:
DateCustomFieldRef customField = customFields[f] as DateCustomFieldRef; // the as operator returns null if the casting did not succeed (that is, customFields[f] is not a DatecustomFieldRef instance
if (customField != null)
{
DateCustomFieldRef dateField = customField;
if (dateField.internalId != null && dateField.internalId == "created_date")
{
createdDate = dateField.value.ToString();
}
}
else
{
var stringField = customFields[f] as StringCustomFieldRef;
if (stringField != null )
{
StringCustomFieldRef tradingNameField = stringField;
if (businessNameField.internalId != null && businessNameField.internalId == "business_name")
{
businessName = businessNameField.value;
}
}
}
But, I believe that there probably exists an even better solution (although I do not know your project, nor your code), but wouldn't it be possible to abstract some things away ?
Perhaps you have a CustomField baseclass, and DateCustomFieldRef and StringCustomFieldRef inherit from that Customfield base-class.
If that's the case, you could create a virtual (or maybe even abstract) method in the CustomField base-class, which is overriden in each child-class, which actually returns the value of that field.
Like this:
public class CustomField<T>
{
public string Internalid
{
get;
set;
}
public T Value
{
get;
set;
}
public virtual string GetStringRepresentation()
{
return Value.ToString();
}
}
public class DateCustomField : CustomField<DateTime>
{
public override string GetStringRepresentation()
{
return Value.ToShortDateString();
}
}
Your code can then look much more simple:
foreach( CustomField f in customFields )
{
if( f.InternalId == "created_date" )
{
createdDate = f.GetStringRepresentation();
}
if( f.InternalId == "business_name" )
{
businessName = f.GetStringRepresentation();
}
}
(The code above could be made more simple and clean, but you'll get the drift.)

C#: Printing all properties of an object [duplicate]

This question already has answers here:
What is the best way to dump entire objects to a log in C#?
(16 answers)
Closed 9 years ago.
Is there a method built into .NET that can write all the properties and such of an object to the console?
One could make use of reflection of course, but I'm curious if this already exists...especially since you can do it in Visual Studio in the Immediate Window. There you can type an object name (while in debug mode), press enter, and it is printed fairly prettily with all its stuff.
Does a method like this exist?
You can use the TypeDescriptor class to do this:
foreach(PropertyDescriptor descriptor in TypeDescriptor.GetProperties(obj))
{
string name = descriptor.Name;
object value = descriptor.GetValue(obj);
Console.WriteLine("{0}={1}", name, value);
}
TypeDescriptor lives in the System.ComponentModel namespace and is the API that Visual Studio uses to display your object in its property browser. It's ultimately based on reflection (as any solution would be), but it provides a pretty good level of abstraction from the reflection API.
Based on the ObjectDumper of the LINQ samples I created a version that dumps each of the properties on its own line.
This Class Sample
namespace MyNamespace
{
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Address Address { get; set; }
public IList<Hobby> Hobbies { get; set; }
}
public class Hobby
{
public string Name { get; set; }
}
public class Address
{
public string Street { get; set; }
public int ZipCode { get; set; }
public string City { get; set; }
}
}
has an output of
{MyNamespace.User}
FirstName: "Arnold"
LastName: "Schwarzenegger"
Address: { }
{MyNamespace.Address}
Street: "6834 Hollywood Blvd"
ZipCode: 90028
City: "Hollywood"
Hobbies: ...
{MyNamespace.Hobby}
Name: "body building"
Here is the code.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
public class ObjectDumper
{
private int _level;
private readonly int _indentSize;
private readonly StringBuilder _stringBuilder;
private readonly List<int> _hashListOfFoundElements;
private ObjectDumper(int indentSize)
{
_indentSize = indentSize;
_stringBuilder = new StringBuilder();
_hashListOfFoundElements = new List<int>();
}
public static string Dump(object element)
{
return Dump(element, 2);
}
public static string Dump(object element, int indentSize)
{
var instance = new ObjectDumper(indentSize);
return instance.DumpElement(element);
}
private string DumpElement(object element)
{
if (element == null || element is ValueType || element is string)
{
Write(FormatValue(element));
}
else
{
var objectType = element.GetType();
if (!typeof(IEnumerable).IsAssignableFrom(objectType))
{
Write("{{{0}}}", objectType.FullName);
_hashListOfFoundElements.Add(element.GetHashCode());
_level++;
}
var enumerableElement = element as IEnumerable;
if (enumerableElement != null)
{
foreach (object item in enumerableElement)
{
if (item is IEnumerable && !(item is string))
{
_level++;
DumpElement(item);
_level--;
}
else
{
if (!AlreadyTouched(item))
DumpElement(item);
else
Write("{{{0}}} <-- bidirectional reference found", item.GetType().FullName);
}
}
}
else
{
MemberInfo[] members = element.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance);
foreach (var memberInfo in members)
{
var fieldInfo = memberInfo as FieldInfo;
var propertyInfo = memberInfo as PropertyInfo;
if (fieldInfo == null && propertyInfo == null)
continue;
var type = fieldInfo != null ? fieldInfo.FieldType : propertyInfo.PropertyType;
object value = fieldInfo != null
? fieldInfo.GetValue(element)
: propertyInfo.GetValue(element, null);
if (type.IsValueType || type == typeof(string))
{
Write("{0}: {1}", memberInfo.Name, FormatValue(value));
}
else
{
var isEnumerable = typeof(IEnumerable).IsAssignableFrom(type);
Write("{0}: {1}", memberInfo.Name, isEnumerable ? "..." : "{ }");
var alreadyTouched = !isEnumerable && AlreadyTouched(value);
_level++;
if (!alreadyTouched)
DumpElement(value);
else
Write("{{{0}}} <-- bidirectional reference found", value.GetType().FullName);
_level--;
}
}
}
if (!typeof(IEnumerable).IsAssignableFrom(objectType))
{
_level--;
}
}
return _stringBuilder.ToString();
}
private bool AlreadyTouched(object value)
{
if (value == null)
return false;
var hash = value.GetHashCode();
for (var i = 0; i < _hashListOfFoundElements.Count; i++)
{
if (_hashListOfFoundElements[i] == hash)
return true;
}
return false;
}
private void Write(string value, params object[] args)
{
var space = new string(' ', _level * _indentSize);
if (args != null)
value = string.Format(value, args);
_stringBuilder.AppendLine(space + value);
}
private string FormatValue(object o)
{
if (o == null)
return ("null");
if (o is DateTime)
return (((DateTime)o).ToShortDateString());
if (o is string)
return string.Format("\"{0}\"", o);
if (o is char && (char)o == '\0')
return string.Empty;
if (o is ValueType)
return (o.ToString());
if (o is IEnumerable)
return ("...");
return ("{ }");
}
}
and you can use it like that:
var dump = ObjectDumper.Dump(user);
Edit
Bi - directional references are now stopped. Therefore the HashCode of an object is stored in a list.
AlreadyTouched fixed (see comments)
FormatValue fixed (see comments)
The ObjectDumper class has been known to do that. I've never confirmed, but I've always suspected that the immediate window uses that.
EDIT: I just realized, that the code for ObjectDumper is actually on your machine. Go to:
C:/Program Files/Microsoft Visual Studio 9.0/Samples/1033/CSharpSamples.zip
This will unzip to a folder called LinqSamples. In there, there's a project called ObjectDumper. Use that.
Maybe via JavaScriptSerializer.Serialize?
Following snippet will do the desired function:
Type t = obj.GetType(); // Where obj is object whose properties you need.
PropertyInfo [] pi = t.GetProperties();
foreach (PropertyInfo p in pi)
{
System.Console.WriteLine(p.Name + " : " + p.GetValue(obj));
}
I think if you write this as extension method you could use it on all type of objects.
Regarding TypeDescriptor from Sean's reply (I can't comment because I have a bad reputation)... one advantage to using TypeDescriptor over GetProperties() is that TypeDescriptor has a mechanism for dynamically attaching properties to objects at runtime and normal reflection will miss these.
For example, when working with PowerShell's PSObject, which can have properties and methods added at runtime, they implemented a custom TypeDescriptor which merges these members in with the standard member set. By using TypeDescriptor, your code doesn't need to be aware of that fact.
Components, controls, and I think maybe DataSets also make use of this API.
This is exactly what reflection is for. I don't think there's a simpler solution, but reflection isn't that code intensive anyway.
Any other solution/library is in the end going to use reflection to introspect the type...
Don't think so. I've always had to write them or use someone else's work to get that info. Has to be reflection as far as i'm aware.
EDIT:
Check this out. I was investigating some debugging on long object graphs and noticed this when i Add Watches, VS throws in this class: Mscorlib_CollectionDebugView<>. It's an internal type for displaying collections nicely for viewing in the watch windows/code debug modes. Now coz it's internal you can reference it, but u can use Reflector to copy (from mscorlib) the code and have your own (the link above has a copy/paste example). Looks really useful.

Categories