I have a class that is declared Internal. It is decorated with various annotations. In particular is the [DisplayName("My Display Name")] annotation. I have some code that will retrieve the value but only works if the class is declared public. I am sort of new to using reflection. I believe I need to specify that the BindingFlags.NonPublic be used but I am not sure where.
LinqPAD code:
void Main()
{
List<SpGetProfileInfoResult> p = new List<SpGetProfileInfoResult>();
p.Add(new SpGetProfileInfoResult() { FName = "Eric" });
p.Add(new SpGetProfileInfoResult() { FName = "Mike" });
p.Dump();
foreach (var item in p)
{
Console.WriteLine(item.DisplayName(i => i.FName));
Console.WriteLine(item.FName);
}
}
public partial class SpGetProfileInfoResult
{
// Uncomment this annotation to see that this part will work
// [System.ComponentModel.DisplayNameAttribute("[BILLTO-FNAME]")]
public string FName { get; set; }
}
public partial class SpGetProfileInfoResult
{
internal class Metadata
{
// This attribute is never available seems.
[System.ComponentModel.DisplayNameAttribute("[BILL-FNAME]")]
public string FName { get; set; }
}
}
public static class Tag
{
public static T GetAttribute<T>(this MemberInfo member, bool isRequired) where T : Attribute
{
var attribute = member.GetCustomAttributes(typeof(T), false).SingleOrDefault();
if (attribute == null && isRequired)
{
throw new ArgumentException(
string.Format(
"The {0} attribute must be defined on member {1}",
typeof(T).Name,
member.Name));
}
return (T)attribute;
}
public static string DisplayName<T>(this T src,Expression<Func<T, object>> propertyExpression)
{
Type metadata = null;
var memberInfo = GetPropertyInformation(propertyExpression.Body);
if (memberInfo == null)
{
throw new ArgumentException(
"No property reference expression was found.",
"propertyExpression");
}
var attr = memberInfo.GetAttribute<DisplayNameAttribute>(false);
if (attr == null)
{
return memberInfo.Name;
}
return attr.DisplayName;
}
public static MemberInfo GetPropertyInformation(Expression propertyExpression)
{
MemberExpression memberExpr = propertyExpression as MemberExpression;
if (memberExpr == null)
{
UnaryExpression unaryExpr = propertyExpression as UnaryExpression;
if (unaryExpr != null && unaryExpr.NodeType == ExpressionType.Convert)
{
memberExpr = unaryExpr.Operand as MemberExpression;
}
}
if (memberExpr != null && memberExpr.Member.MemberType == MemberTypes.Property)
{
return memberExpr.Member;
}
return null;
}
}
Usage:
If you don't have LinqPAD, you should download it then you can test this pretty easily by just creating a new C# Program in LinkPAD
Debug.WriteLine(item.DisplayName(i => i.FName));
So it looks like you want to be able to decorate existing members of a partial class, by providing metadata in a separate partial piece. There's no built-in mechanism for that (see eg this question and the classes mentioned in the answer), but if you're willing to stick to a convention, you can roll your own:
So suppose we have
public partial class SpGetProfileInfoResult
{
public string FName { get; set; }
}
in a partial piece we can't change, and
public partial class SpGetProfileInfoResult
{
internal class Metadata
{
[System.ComponentModel.DisplayNameAttribute("[BILL-FNAME]")]
public string FName { get; set; }
}
}
in a partial piece we can change. You already have most of the pieces: in DisplayName(), you successfully determine that we are looking at the FName property; you then look for a DisplayNameAttribute on T.FName, but there isn't one, so that's where it stops.
What you need to do is, in the case where you don't find the attribute you need,
var attr = memberInfo.GetAttribute<DisplayNameAttribute>(false);
if (attr == null)
{
Look for a nested class named Metadata - note here is one place we use BindingFlags.NonPublic
// Try and get a nested metadata class
var metadataType = typeof(T)
.GetNestedType("Metadata",
BindingFlags.Public | BindingFlags.NonPublic);
If we find one:
if (metadataType != null)
{
Look for a member of the same name as was originally being talked about (BindingFlags.NonPublic again)
var membersOnMetadataType = metadataType.GetMember(memberInfo.Name,
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic);
If there is one, use your helper method, but this time pass it the metadata type's member:
if (membersOnMetadataType.Any())
{
var attrOnMetadataType = membersOnMetadataType[0]
.GetAttribute<DisplayNameAttribute>(false);
return attrOnMetadataType.DisplayName;
(I've omitted a final nullity check here, as well as closing the control flow)
Depending on how distasteful you find that "Metadata" string, you could instead do something declarative with attributes:
have a class-level attribute that goes on SpGetProfileInfoResult (the piece you can change) that points at its Metadata using typeof (this is the approach taken by System.ComponentModel), or
have a class-level attribute that goes on Metadata, to have it claim 'I am a metadata type'. Then instead of searching for a nested class named a fixed string, we would instead search for a nested class having this particular attribute.
After working on this for a while I came up with a Hack. I am sure someone out there can help me clean this up a bit, but this is what I found works. I had to add the "Metadata" nested class to the DeclaringType and then do a GetMember on that result, which returns a collection of members.
public static string DisplayName<T>(this T src, Expression<Func<T, object>> propertyExpression)
{
var memberInfo = GetPropertyInformation(propertyExpression.Body);
var mytype = src.GetType();
string strType = mytype.Name + "+Metadata";
var metaType = Type.GetType(strType);
MemberInfo[] mem = metaType.GetMember(memberInfo.Name);
var att = mem[0].GetCustomAttributes(typeof(DisplayNameAttribute), true).FirstOrDefault() as DisplayNameAttribute;
if (att == null)
return memberInfo.Name;
else
return att.DisplayName;
}
I won't try to debug your code because you're using some classes that I'm not familiar with.
One thing I do know is that MemberInfo does not have a GetAttribute() function. You must be using an extension method there.
However I can tell you that you don't need any special bindingflags just because the type is internal. Only the visibility of the member is important, and in this case it's public.
using System;
using System.ComponentModel;
namespace ConsoleApplication1
{
internal class Metadata
{
[DisplayName("[BILL-FNAME]")]
public string FName { get; set; }
}
class Program
{
static void Main()
{
var memberInfo = typeof(Metadata).GetMember("FName")[0];
var atrributes = memberInfo.GetCustomAttributes(false);
Console.WriteLine(atrributes[0].GetType().Name);
}
}
}
Output:
DisplayNameAttribute
Related
So, I thaught I have a solution for getting a PropertyInfo when having a concrete class, and a PropertyInfo for an interface implemented by the concrete class. Here is the code:
public static PropertyInfo GetImplementingProperty(Type concreteType, PropertyInfo interfaceProperty)
{
// do some region parameter check, skipped
var interfaceType = interfaceProperty.DeclaringType;
//use the set method if we have a write only property
var getCorrectMethod = interfaceProperty.GetGetMethod() == null
? (Func<PropertyInfo, MethodInfo>) (p => p.GetSetMethod(true))
: p => p.GetGetMethod(true);
var propertyMethod = getCorrectMethod(interfaceProperty);
var mapping = concreteType.GetInterfaceMap(interfaceType);
MethodInfo targetMethod = null;
for (var i = 0; i < mapping.InterfaceMethods.Length; i++)
{
if (mapping.InterfaceMethods[i] == propertyMethod)
{
targetMethod = mapping.TargetMethods[i];
break;
}
}
foreach (var property in concreteType.GetProperties(
BindingFlags.Instance | BindingFlags.GetProperty |
BindingFlags.Public | BindingFlags.NonPublic)) // include non-public!
{
if (targetMethod == getCorrectMethod(property)) // include non-public!
{
return property;
}
}
throw new InvalidOperationException("The property {0} defined on the interface {1} has not been found on the class {2}. That should never happen."
.FormatText(interfaceProperty.Name, interfaceProperty.DeclaringType.FullName, concreteType.FullName));
}
Unfortunately I found a case where it fails, and I am unsure how to fix this.
So I have in a dll a class:
public abstract class BaseClass
{
public Guid ConfigId { get; set; }
public virtual Guid ConfigId2 { get; set; }
}
Then in another dll I do:
interface INamed
{
Guid ConfigId { get; }
Guid ConfigId2 { get; }
}
private class SuperClass : BaseClass, INamed
{
}
Now
ReflectionHelper.GetImplementingProperty(typeof(SuperClass), typeof(INamed).GetProperty("ConfigId2")); // this works
ReflectionHelper.GetImplementingProperty(typeof(SuperClass), typeof(INamed).GetProperty("ConfigId")); // this fails
Any idea how do I match up the ConfigId property to the Base class proprety definition?
PS. I have attributes on the concrete class properties, thats why i need to get those.
Any help appreciated!
You need to add BindingFlags.FlattenHierarchy to your GetProperties call in order to get parent class properties. See the documentation at https://msdn.microsoft.com/en-us/library/kyaxdd3x(v=vs.110).aspx
I want to write an extension method to get the value of the MaximumLength property on the StringLength attribute.
For example, I have a class:
public class Person
{
[StringLength(MaximumLength=1000)]
public string Name { get; set; }
}
I want to be able to do this:
Person person = new Person();
int maxLength = person.Name.GetMaxLength();
Would this be possible using some sort of reflection?
If you use LINQ expressions, you can pull out the information via reflection with slightly different syntax (and you get to avoid defining an extension method on a commonly used string type):
public class StringLength : Attribute
{
public int MaximumLength;
public static int Get<TProperty>(Expression<Func<TProperty>> propertyLambda)
{
MemberExpression member = propertyLambda.Body as MemberExpression;
if (member == null)
throw new ArgumentException(string.Format(
"Expression '{0}' refers to a method, not a property.",
propertyLambda.ToString()));
PropertyInfo propInfo = member.Member as PropertyInfo;
if (propInfo == null)
throw new ArgumentException(string.Format(
"Expression '{0}' refers to a field, not a property.",
propertyLambda.ToString()));
var stringLengthAttributes = propInfo.GetCustomAttributes(typeof(StringLength), true);
if (stringLengthAttributes.Length > 0)
return ((StringLength)stringLengthAttributes[0]).MaximumLength;
return -1;
}
}
So your Person class might be:
public class Person
{
[StringLength(MaximumLength=1000)]
public string Name { get; set; }
public string OtherName { get; set; }
}
Your usage might look like:
Person person = new Person();
int maxLength = StringLength.Get(() => person.Name);
Console.WriteLine(maxLength); //1000
maxLength = StringLength.Get(() => person.OtherName);
Console.WriteLine(maxLength); //-1
You can return something other than -1 for a property that didn't have that attribute defined. You weren't specific, but that's easy to change.
This may not be the nicest way to do this, but if you dont mind suppling the property name you need to get the Attribute value for you could use something like
public static class StringExtensions
{
public static int GetMaxLength<T>(this T obj, string propertyName) where T : class
{
if (obj != null)
{
var attrib = (StringLengthAttribute)obj.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance)
.GetCustomAttribute(typeof(StringLengthAttribute), false);
if (attrib != null)
{
return attrib.MaximumLength;
}
}
return -1;
}
}
Usage:
Person person = new Person();
int maxLength = person.GetMaxLength("Name");
Otherwise using a function like Chris Sinclair mentioned in his comment would work nicely
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Finding the Variable Name passed to a Function in C#
public new Dictionary<string, string> Attributes { get; set; }
public string StringAttributes = string.Empty;
public int? MaxLength { get; set; }
public int? Size { get; set; }
public int? Width { get; set; }
public int? Height { get; set; }
protected override void OnInit(EventArgs e) {
Attributes = new Dictionary<string, string>();
Attributes.Add("MaxLength", MaxLength.ToString());
Attributes.Add("Size", Size.ToString());
Attributes.Add("Width", Width.ToString());
Attributes.Add("Height", Height.ToString());
base.OnInit(e);
}
protected override void OnPreRender(EventArgs e) {
if (Attributes != null) {
StringBuilder attributes = new StringBuilder();
foreach (var item in Attributes) {
if (!string.IsNullOrWhiteSpace(item.Value)) {
attributes.Append(item.Key + "=\"" + item.Value + "\" ");
}
}
StringAttributes = attributes.ToString();
}
}
The problem here is, instead of using Attributes.Add("MaxLength", MaxLength.ToString()); and repeat the same process for other properties, could we not just make a function that is also able to add values to the dictionary, where the keys to be added are their variable names?
Say,
public void addAttribute(object variable){
Attributes = new Dictionary<string, string>();
Attributes.Add(variable.Name, variable.Value);
}...
I guess this is also possible to do with reflection, getting all the nullable properties and looping through them then adding each to the dictionary... But for as long as there are any other ways, we would not stick to reflection.
But if reflection is the only choice, then another problem now would be how to get the nullable properties of the class...
Any help would be greatly appreciated. Thank you.
I can't think of way to do it without reflection.
In order to get all the nullable properties you can you similar code to this:
GetType().GetProperties()
.Where(property =>
property.PropertyType.IsGenericType &&
property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
Usage example that fills attributes dictionary:
PropertyInfo[] typeProperties = GetType().GetProperties();
var nullableProperties = typeProperties.Where(property =>
property.PropertyType.IsGenericType &&
property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>));
var attributes = new Dictionary<string, string>();
foreach (var nullableProperty in nullableProperties)
{
object value = nullableProperty.GetValue(this,null);
attributes.Add(nullableProperty.Name, value == null ?
string.Empty : value.ToString());
}
I'm not sure I fully understand your question without more context, but perhaps this is helpful
If the concern is over reflection overhead for multiple invocations:
Cache that information.
try EmitMapper to fill in values
try AutoMapper to fill in the values
If the problem is getting a variable name via strongly typed compilation then you can use
The Member class I saw on a post from Oliver Hhanappi. Examples of its use are here on my blog
Below is my complete solution. I would say your best bet is to use reflection, as what you're asking is sort of a meta-task. As far as how do you know which properties to add, I would suggest defining your own attribute and applying it to the fields/properties that you want to inspect.
Usage:
Dictionary<string, string> attributes = Inspector<MyClass>.Inspect(target);
The reflection in my sample code is executed once per type inspected, as it is executed within the static constructor of my generic Inspect class:
// apply this attribute to any properties or fields that you want added to the attributes dictionary
[AttributeUsage(
AttributeTargets.Property |
AttributeTargets.Field |
AttributeTargets.Class |
AttributeTargets.Struct |
AttributeTargets.Interface,
AllowMultiple = true, Inherited = true)]
public class InspectAttribute : Attribute
{
// optionally specify the member name explicitly, for use on classes, structs, and interfaces
public string MemberName { get; set; }
public InspectAttribute() { }
public InspectAttribute(string memberName)
{
this.MemberName = memberName;
}
}
public class Inspector<T>
{
// Inspector is a generic class, therefore there will be a separate instance of the _InspectActions variable per type
private static List<Action<Dictionary<string, string>, T>> _InspectActions;
static Inspector()
{
_InspectActions = new List<Action<Dictionary<string, string>, T>>();
foreach (MemberInfo m in GetInspectableMembers(typeof(T)))
{
switch (m.MemberType)
{
case MemberTypes.Property:
{
// declare a separate variable for variable scope with anonymous delegate
PropertyInfo member = m as PropertyInfo;
// create an action delegate to add an entry to the attributes dictionary using the property name and value
_InspectActions.Add(
delegate(Dictionary<string, string> attributes, T item)
{
object value = member.GetValue(item, null);
attributes.Add(member.Name, (value == null) ? "[null]" : value.ToString());
});
}
break;
case MemberTypes.Field:
{
// declare a separate variable for variable scope with anonymous delegate
FieldInfo member = m as FieldInfo;
// need to create a separate variable so that delegates do not share the same variable
// create an action delegate to add an entry to the attributes dictionary using the field name and value
_InspectActions.Add(
delegate(Dictionary<string, string> attributes, T item)
{
object value = member.GetValue(item);
attributes.Add(member.Name, (value == null) ? "[null]" : value.ToString());
});
}
break;
default:
// for all other member types, do nothing
break;
}
}
}
private static IEnumerable<MemberInfo> GetInspectableMembers(Type t)
{
// get all instance fields and properties
foreach (MemberInfo member in t.GetMembers(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.GetField | BindingFlags.GetProperty))
{
// check if the current member is decorated with an Inspect attribute
object[] inspectAttributes = member.GetCustomAttributes(typeof(InspectAttribute), true);
if (inspectAttributes != null && inspectAttributes.Length > 0)
{
yield return member;
}
}
// now look for any Inspect attributes defined at the type level
InspectAttribute[] typeLevelInspectAttributes = (InspectAttribute[])t.GetCustomAttributes(typeof(InspectAttribute), true);
if (typeLevelInspectAttributes != null && typeLevelInspectAttributes.Length > 0)
{
foreach (InspectAttribute attribute in typeLevelInspectAttributes)
{
// search for members matching the name provided by the Inspect attribute
MemberInfo[] members = t.GetMember(attribute.MemberName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.FlattenHierarchy);
if (members != null && members.Length > 0)
{
foreach (MemberInfo member in members)
{
yield return member;
}
}
}
}
}
public static Dictionary<string, string> Inspect(T item)
{
// create a new attributes dictionary
Dictionary<string, string> attributes = new Dictionary<string, string>();
foreach (Action<Dictionary<string, string>, T> inspectAction in _InspectActions)
{
// execute each "inspect" action.
// This will execute the delegates we created earlier, causing entries to be added to the dictionary
inspectAction(attributes, item);
}
return attributes;
}
}
public class BasePage
{
public int? SomeValue { get; set; }
}
// example class with properties decorated with the Inspect attribute
[Inspect("SomeValue")] // also inspect the "SomeValue" property from the BasePage class
public class MyPage : BasePage
{
[Inspect]
public int? MaxLength { get; set; }
[Inspect]
public int? Size { get; set; }
[Inspect]
public int? Width { get; set; }
[Inspect]
public int? Height { get; set; }
public string GenerateAttributeString()
{
System.Text.StringBuilder attributes = new System.Text.StringBuilder();
foreach (KeyValuePair<string, string> item in Inspector<MyPage>.Inspect(this))
{
attributes.Append(item.Key + "=\"" + item.Value + "\" ");
}
return attributes.ToString();
}
}
You can use the following function to extract out the public Nullable properties from a class into the format your looking for. It also calls the getter method for the value.
This is using the same reflection use that #Elisha talked about. Also it does a .ToString() call to the value returned by the getter.
IDictionary<string, string> GetProps<T>(T DataObject)
{
if(null == DataObject)
return new Dictionary<string, string>();
var nullableProperties =
from property in typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)
from accessor in property.GetAccessors(false)
let returnType = accessor.ReturnType
where returnType.IsGenericType
&& returnType.GetGenericTypeDefinition() == typeof(Nullable<>)
&& accessor.GetParameters().Length == 0
select new { Name=property.Name, Getter=accessor};
return nullableProperties.ToDictionary(
x => x.Name,
x => x.Getter.Invoke(DataObject, null).ToString());
}
Yesterday I ran into an Issue while developing a Web Part (This question is not about webpart but about C#). Little background about the Issue. I have a code that load the WebPart using the Reflection, In which I got the AmbiguousMatchException. To reproduce it try the below code
public class TypeA
{
public virtual int Height { get; set; }
}
public class TypeB : TypeA
{
public String Height { get; set; }
}
public class Class1 : TypeB
{
}
Assembly oAssemblyCurrent = Assembly.GetExecutingAssembly();
Type oType2 = oAssemblyCurrent.GetType("AmbigousMatchReflection.Class1");
PropertyInfo oPropertyInfo2 = oType2.GetProperty("Height");//Throws AmbiguousMatchException
oPropertyInfo2 = oType2.GetProperty("Height",
BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance); // I tried this code Neither these BindingFlags or any other didnt help
I wanted to know the BindingFlag to Fetch the Height Property. You will have the question of why I wanted to create another Height Property that is already there in the Base class. That is how the Microsoft.SharePoint.WebPartPages.PageViewerWebPart was designed check the Height property of the PageViewerWebPart class.
There are two Height properties there, and neither of them are declared by Class1 which you're calling GetProperty on.
Now, would it be fair to say you're looking for "the Height property declared as far down the type hiearchy as possible"? If so, here's some code to find it:
using System;
using System.Diagnostics;
using System.Reflection;
public class TypeA
{
public virtual int Height { get; set; }
}
public class TypeB : TypeA
{
public new String Height { get; set; }
}
public class Class1 : TypeB
{
}
class Test
{
static void Main()
{
Type type = typeof(Class1);
Console.WriteLine(GetLowestProperty(type, "Height").DeclaringType);
}
static PropertyInfo GetLowestProperty(Type type, string name)
{
while (type != null)
{
var property = type.GetProperty(name, BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.Instance);
if (property != null)
{
return property;
}
type = type.BaseType;
}
return null;
}
}
Note that if you know the return types will be different, it may be worth simplifying the code as shown in sambo99's answer. That would make it quite brittle though - changing the return type later could then cause bugs which would only be found at execution time. Ouch. I'd say that by the time you've done this you're in a brittle situation anyway :)
See the following example:
class Foo {
public float Height { get; set; }
}
class Bar : Foo {
public int Height { get; set; }
}
class BarBar : Bar { }
class Foo2 : Foo{
public float Height { get; set; }
}
class BarBar2 : Foo2 { }
static void Main(string[] args) {
// works
var p = typeof(BarBar).GetProperty("Height", typeof(float), Type.EmptyTypes);
// works
var p2 = typeof(BarBar).BaseType.GetProperty("Height", BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);
// works
var p3 = typeof(BarBar2).GetProperty("Height");
// fails
var p4 = typeof(BarBar).GetProperty("Height");
Console.WriteLine(p);
}
You get an AmbiguousMatchException if a two or more properties with the differing return types and the same name live in your inheritance chain.
Stuff resolves just fine if you override an implementation (using new or override) and maintain the return type.
You can force reflection only to look at the properties for a particular type.
Obviously there are two properties that match the name you have given of "Height", one with return type int, and another string., Just add the return type as the second parameter tot the GetPropertyCall depending on which you want returning and this ambiguity should disappear.
I created two extension methods extending on Jon Skeet's answer. You can place those in any public static class.
Edit: Removed the MissingMemberException to behave more like the default .NET implementations which return null on failure.
Usage:
var field = type.GetFieldUnambiguous(type, "FieldName", bindingFlags);
var property = type.GetPropertyUnambiguous(type, "PropertyName", bindingFlags);
Implementation:
public static FieldInfo GetFieldUnambiguous(this Type type, string name, BindingFlags flags)
{
if (type == null) throw new ArgumentNullException(nameof(type));
if (name == null) throw new ArgumentNullException(nameof(name));
flags |= BindingFlags.DeclaredOnly;
while (type != null)
{
var field = type.GetField(name, flags);
if (field != null)
{
return field;
}
type = type.BaseType;
}
return null;
}
public static PropertyInfo GetPropertyUnambiguous(this Type type, string name, BindingFlags flags
{
if (type == null) throw new ArgumentNullException(nameof(type));
if (name == null) throw new ArgumentNullException(nameof(name));
flags |= BindingFlags.DeclaredOnly;
while (type != null)
{
var property = type.GetProperty(name, flags);
if (property != null)
{
return property;
}
type = type.BaseType;
}
return null;
}
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.