How to get Attribute Value in C# for the class property - c#

[System.AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
sealed class ColumnName : Attribute
{
// See the attribute guidelines at
// http://go.microsoft.com/fwlink/?LinkId=85236
readonly string Column;
// This is a positional argument
public ColumnName(string columnName)
{
this.Column = columnName;
}
}
public class Comment
{
[ColumnName("ID1")]
public int Id;
[ColumnName("NAME1")]
public string Name;
[ColumnName("TEST1")]
public string Test;
}
In this code you can see I have create a class comment which have an attribute ColumnName. ColumnName is my custom class which I used to define the attirubte.
Now I am looking for a sollution to find the ColumnName value for all the properties.
public static List<T> ExecuteReader<T>(string str)
{
var res = typeof(T);
return new List<T>();
}
I tried run some Stack Overflow code on my issue but it doesn't work well. What thing I am missing in my code?

Given
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false, AllowMultiple = true)]
public sealed class ColumnNameAttribute : Attribute
{
public readonly string Column;
public ColumnNameAttribute(string columnName)
{
this.Column = columnName;
}
}
(by convention attributes should have a name ending in Attribute, and note that I've restricted the AttributeTargets to Propertyes and Fields) you can
public static class ColumnsCache<T>
{
public static readonly IReadOnlyDictionary<MemberInfo, string> Columns = BuildColumnsDictionary();
public static Dictionary<MemberInfo, string> BuildColumnsDictionary()
{
var dict = new Dictionary<MemberInfo, string>();
var members = typeof(T).GetMembers(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.MemberType == MemberTypes.Field || x.MemberType == MemberTypes.Property);
foreach (MemberInfo member in members)
{
var attr = member.GetCustomAttribute<ColumnNameAttribute>(true);
if (attr != null)
{
dict.Add(member, attr.Column);
}
}
return dict;
}
}
(note the trick: we are caching the list of column names (and of fields/properties having the ColumnNameAttribute) through the usage of a generic static class. The .NET runtime will create various distinct ColumnsCache<T1>, ColumnsCache<T2>, ColumnsCache<T3>, each one with a different dictionary of columns)
Then you can
var cols = ColumnsCache<Comment>.Columns;
var colNames = cols.Values;
The cols variable will reference a dictionary MemberInfo -> string (colum name), while the colNames is a IEnumerable<string> with only the column names. If you want to use reflection with a MemberInfo you have to check if the MemberInfo is a FieldInfo or a PropertyInfo, cast it and use the FieldInfo or PropertyInfo.

Related

C# Get constants from nested classes with reflection

I'd like to create a List from all of the constants in the nested classes.
public struct SomePair
{
public string Name, Value;
public SomePair(string name, string value)
{
Name = name;
Value = value;
}
}
private static MemberInfo[] GetClasses() => typeof(MainFoo).GetMembers(BindingFlags.Public);
private static List<Type> GetClassTypes() => GetClasses().Select(c=>c.GetType()).ToList();
public static class MainFoo
{
// The return value should contain the information about the SomeConstant's from both Errors and Foo.
public static List<LocalizationPair> Dump()
{
List<SomePair> Dump = new List<SomePair>();
var classes = GetClassTypes();
foreach (Type cls in classes)
{
var constants = cls.GetFields(BindingFlags.Public); // <<< Is always empty...
foreach (FieldInfo constant in constants)
{
Dump.Add(new SomePair(
$"{cls.Name}.{constant.Name}",
constant.GetValue(cls).ToString()
));
}
}
return Dump;
}
public static class Errors
{
public constant string SomeConstant = "a";
}
public static class Foo
{
public constant string SomeConstant = "a";
}
}
I'm able to get a list of all classes and a list of all class-types but once I try to use GetMember() on those, it returns nothing.
Use GetNestedTypes() instead with the correct BindingFlags for public constants:
var nestedTypes = typeof(MainFoo).GetNestedTypes(BindingFlags.Public);
foreach (Type type in nestedTypes)
{
FieldInfo[] constants = type.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
// <do stuff here>
}

Parse string attributes of class into string array [duplicate]

I have class and properties in there. Some properties can be marked attribute (it's my LocalizedDisplayName inherits from DisplayNameAttribute).
This is method for get all properties of class:
private void FillAttribute()
{
Type type = typeof (NormDoc);
PropertyInfo[] propertyInfos = type.GetProperties();
foreach (var propertyInfo in propertyInfos)
{
...
}
}
I want to add properties of class in the listbox which marked LocalizedDisplayName and display value of attribute in the listbox. How can I do this?
EDIT
This is LocalizedDisplayNameAttribute:
public class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
public LocalizedDisplayNameAttribute(string resourceId)
: base(GetMessageFromResource(resourceId))
{ }
private static string GetMessageFromResource(string resourceId)
{
var test =Thread.CurrentThread.CurrentCulture;
ResourceManager manager = new ResourceManager("EArchive.Data.Resources.DataResource", Assembly.GetExecutingAssembly());
return manager.GetString(resourceId);
}
}
I want to get string from resource file.
Thanks.
It's probably easiest to use IsDefined:
var properties = type.GetProperties()
.Where(prop => prop.IsDefined(typeof(LocalizedDisplayNameAttribute), false));
To get the values themselves, you'd use:
var attributes = (LocalizedDisplayNameAttribute[])
prop.GetCustomAttributes(typeof(LocalizedDisplayNameAttribute), false);

Application and User Settings C# [duplicate]

I have a class, lets call it Book with a property called Name. With that property, I have an attribute associated with it.
public class Book
{
[Author("AuthorName")]
public string Name
{
get; private set;
}
}
In my main method, I'm using reflection and wish to get key value pair of each attribute for each property. So in this example, I'd expect to see "Author" for attribute name and "AuthorName" for the attribute value.
Question: How do I get the attribute name and value on my properties using Reflection?
Use typeof(Book).GetProperties() to get an array of PropertyInfo instances. Then use GetCustomAttributes() on each PropertyInfo to see if any of them have the Author Attribute type. If they do, you can get the name of the property from the property info and the attribute values from the attribute.
Something along these lines to scan a type for properties that have a specific attribute type and to return data in a dictionary (note that this can be made more dynamic by passing types into the routine):
public static Dictionary<string, string> GetAuthors()
{
Dictionary<string, string> _dict = new Dictionary<string, string>();
PropertyInfo[] props = typeof(Book).GetProperties();
foreach (PropertyInfo prop in props)
{
object[] attrs = prop.GetCustomAttributes(true);
foreach (object attr in attrs)
{
AuthorAttribute authAttr = attr as AuthorAttribute;
if (authAttr != null)
{
string propName = prop.Name;
string auth = authAttr.Name;
_dict.Add(propName, auth);
}
}
}
return _dict;
}
To get all attributes of a property in a dictionary use this:
typeof(Book)
.GetProperty("Name")
.GetCustomAttributes(false)
.ToDictionary(a => a.GetType().Name, a => a);
remember to change from false to true if you want to include inheritted attributes as well.
If you just want one specific Attribute value For instance Display Attribute you can use the following code:
var pInfo = typeof(Book).GetProperty("Name")
.GetCustomAttribute<DisplayAttribute>();
var name = pInfo.Name;
I have solved similar problems by writing a Generic Extension Property Attribute Helper:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
public static class AttributeHelper
{
public static TValue GetPropertyAttributeValue<T, TOut, TAttribute, TValue>(
Expression<Func<T, TOut>> propertyExpression,
Func<TAttribute, TValue> valueSelector)
where TAttribute : Attribute
{
var expression = (MemberExpression) propertyExpression.Body;
var propertyInfo = (PropertyInfo) expression.Member;
var attr = propertyInfo.GetCustomAttributes(typeof(TAttribute), true).FirstOrDefault() as TAttribute;
return attr != null ? valueSelector(attr) : default(TValue);
}
}
Usage:
var author = AttributeHelper.GetPropertyAttributeValue<Book, string, AuthorAttribute, string>(prop => prop.Name, attr => attr.Author);
// author = "AuthorName"
You can use GetCustomAttributesData() and GetCustomAttributes():
var attributeData = typeof(Book).GetProperty("Name").GetCustomAttributesData();
var attributes = typeof(Book).GetProperty("Name").GetCustomAttributes(false);
If you mean "for attributes that take one parameter, list the attribute-names and the parameter-value", then this is easier in .NET 4.5 via the CustomAttributeData API:
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
public static class Program
{
static void Main()
{
PropertyInfo prop = typeof(Foo).GetProperty("Bar");
var vals = GetPropertyAttributes(prop);
// has: DisplayName = "abc", Browsable = false
}
public static Dictionary<string, object> GetPropertyAttributes(PropertyInfo property)
{
Dictionary<string, object> attribs = new Dictionary<string, object>();
// look for attributes that takes one constructor argument
foreach (CustomAttributeData attribData in property.GetCustomAttributesData())
{
if(attribData.ConstructorArguments.Count == 1)
{
string typeName = attribData.Constructor.DeclaringType.Name;
if (typeName.EndsWith("Attribute")) typeName = typeName.Substring(0, typeName.Length - 9);
attribs[typeName] = attribData.ConstructorArguments[0].Value;
}
}
return attribs;
}
}
class Foo
{
[DisplayName("abc")]
[Browsable(false)]
public string Bar { get; set; }
}
private static Dictionary<string, string> GetAuthors()
{
return typeof(Book).GetProperties()
.SelectMany(prop => prop.GetCustomAttributes())
.OfType<AuthorAttribute>()
.ToDictionary(a => a.GetType().Name.Replace("Attribute", ""), a => a.Name);
}
Example using generics (target framework 4.5)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
private static Dictionary<string, string> GetAttribute<TAttribute, TType>(
Func<TAttribute, string> valueFunc)
where TAttribute : Attribute
{
return typeof(TType).GetProperties()
.SelectMany(p => p.GetCustomAttributes())
.OfType<TAttribute>()
.ToDictionary(a => a.GetType().Name.Replace("Attribute", ""), valueFunc);
}
Usage
var dictionary = GetAttribute<AuthorAttribute, Book>(a => a.Name);
public static class PropertyInfoExtensions
{
public static TValue GetAttributValue<TAttribute, TValue>(this PropertyInfo prop, Func<TAttribute, TValue> value) where TAttribute : Attribute
{
var att = prop.GetCustomAttributes(
typeof(TAttribute), true
).FirstOrDefault() as TAttribute;
if (att != null)
{
return value(att);
}
return default(TValue);
}
}
Usage:
//get class properties with attribute [AuthorAttribute]
var props = typeof(Book).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(AuthorAttribute)));
foreach (var prop in props)
{
string value = prop.GetAttributValue((AuthorAttribute a) => a.Name);
}
or:
//get class properties with attribute [AuthorAttribute]
var props = typeof(Book).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(AuthorAttribute)));
IList<string> values = props.Select(prop => prop.GetAttributValue((AuthorAttribute a) => a.Name)).Where(attr => attr != null).ToList();
While the above most upvoted answers definitely work, I'd suggest using a slightly different approach in some cases.
If your class has multiple properties with always the same attribute and you want to get those attributes sorted into a dictionary, here is how:
var dict = typeof(Book).GetProperties().ToDictionary(p => p.Name, p => p.GetCustomAttributes(typeof(AuthorName), false).Select(a => (AuthorName)a).FirstOrDefault());
This still uses cast but ensures that the cast will always work as you will only get the custom attributes of the type "AuthorName".
If you had multiple Attributes above answers would get a cast exception.
Here are some static methods you can use to get the MaxLength, or any other attribute.
using System;
using System.Linq;
using System.Reflection;
using System.ComponentModel.DataAnnotations;
using System.Linq.Expressions;
public static class AttributeHelpers {
public static Int32 GetMaxLength<T>(Expression<Func<T,string>> propertyExpression) {
return GetPropertyAttributeValue<T,string,MaxLengthAttribute,Int32>(propertyExpression,attr => attr.Length);
}
//Optional Extension method
public static Int32 GetMaxLength<T>(this T instance,Expression<Func<T,string>> propertyExpression) {
return GetMaxLength<T>(propertyExpression);
}
//Required generic method to get any property attribute from any class
public static TValue GetPropertyAttributeValue<T, TOut, TAttribute, TValue>(Expression<Func<T,TOut>> propertyExpression,Func<TAttribute,TValue> valueSelector) where TAttribute : Attribute {
var expression = (MemberExpression)propertyExpression.Body;
var propertyInfo = (PropertyInfo)expression.Member;
var attr = propertyInfo.GetCustomAttributes(typeof(TAttribute),true).FirstOrDefault() as TAttribute;
if (attr==null) {
throw new MissingMemberException(typeof(T).Name+"."+propertyInfo.Name,typeof(TAttribute).Name);
}
return valueSelector(attr);
}
}
Using the static method...
var length = AttributeHelpers.GetMaxLength<Player>(x => x.PlayerName);
Or using the optional extension method on an instance...
var player = new Player();
var length = player.GetMaxLength(x => x.PlayerName);
Or using the full static method for any other attribute (StringLength for example)...
var length = AttributeHelpers.GetPropertyAttributeValue<Player,string,StringLengthAttribute,Int32>(prop => prop.PlayerName,attr => attr.MaximumLength);
Inspired by the Mikael Engver's answer.
I wrote this into a dynamic method since I use lots of attributes throughout my application. Method:
public static dynamic GetAttribute(Type objectType, string propertyName, Type attrType)
{
//get the property
var property = objectType.GetProperty(propertyName);
//check for object relation
return property.GetCustomAttributes().FirstOrDefault(x => x.GetType() == attrType);
}
Usage:
var objectRelAttr = GetAttribute(typeof(Person), "Country", typeof(ObjectRelationAttribute));
var displayNameAttr = GetAttribute(typeof(Product), "Category", typeof(DisplayNameAttribute));
Hope this helps anyone
Necromancing.
For those that still have to maintain .NET 2.0, or those that want to do it without LINQ:
public static object GetAttribute(System.Reflection.MemberInfo mi, System.Type t)
{
object[] objs = mi.GetCustomAttributes(t, true);
if (objs == null || objs.Length < 1)
return null;
return objs[0];
}
public static T GetAttribute<T>(System.Reflection.MemberInfo mi)
{
return (T)GetAttribute(mi, typeof(T));
}
public delegate TResult GetValue_t<in T, out TResult>(T arg1);
public static TValue GetAttributValue<TAttribute, TValue>(System.Reflection.MemberInfo mi, GetValue_t<TAttribute, TValue> value) where TAttribute : System.Attribute
{
TAttribute[] objAtts = (TAttribute[])mi.GetCustomAttributes(typeof(TAttribute), true);
TAttribute att = (objAtts == null || objAtts.Length < 1) ? default(TAttribute) : objAtts[0];
// TAttribute att = (TAttribute)GetAttribute(mi, typeof(TAttribute));
if (att != null)
{
return value(att);
}
return default(TValue);
}
Example usage:
System.Reflection.FieldInfo fi = t.GetField("PrintBackground");
wkHtmlOptionNameAttribute att = GetAttribute<wkHtmlOptionNameAttribute>(fi);
string name = GetAttributValue<wkHtmlOptionNameAttribute, string>(fi, delegate(wkHtmlOptionNameAttribute a){ return a.Name;});
or simply
string aname = GetAttributValue<wkHtmlOptionNameAttribute, string>(fi, a => a.Name );
Just looking for the right place to put this piece of code.
let's say you have the following property:
[Display(Name = "Solar Radiation (Average)", ShortName = "SolarRadiationAvg")]
public int SolarRadiationAvgSensorId { get; set; }
And you want to get the ShortName value. You can do:
((DisplayAttribute)(typeof(SensorsModel).GetProperty(SolarRadiationAvgSensorId).GetCustomAttribute(typeof(DisplayAttribute)))).ShortName;
Or to make it general:
internal static string GetPropertyAttributeShortName(string propertyName)
{
return ((DisplayAttribute)(typeof(SensorsModel).GetProperty(propertyName).GetCustomAttribute(typeof(DisplayAttribute)))).ShortName;
}
foreach (var p in model.GetType().GetProperties())
{
var valueOfDisplay =
p.GetCustomAttributesData()
.Any(a => a.AttributeType.Name == "DisplayNameAttribute") ?
p.GetCustomAttribute<DisplayNameAttribute>().DisplayName :
p.Name;
}
In this example I used DisplayName instead of Author because it has a field named 'DisplayName' to be shown with a value.
to get attribute from enum, i'm using :
public enum ExceptionCodes
{
[ExceptionCode(1000)]
InternalError,
}
public static (int code, string message) Translate(ExceptionCodes code)
{
return code.GetType()
.GetField(Enum.GetName(typeof(ExceptionCodes), code))
.GetCustomAttributes(false).Where((attr) =>
{
return (attr is ExceptionCodeAttribute);
}).Select(customAttr =>
{
var attr = (customAttr as ExceptionCodeAttribute);
return (attr.Code, attr.FriendlyMessage);
}).FirstOrDefault();
}
// Using
var _message = Translate(code);
If you want get property having the custom Attribute then please try the following:
IEnumerable propertyInfos = properties.GetType().GetProperties();
PropertyInfo p = propertyInfos.Where(x => x.GetCustomAttribute() != null);

Generic Reflection of static types with code reuse

I have a method that iterates the fields of a class, returning their values as a CSV. I need a way to give classes access to this method in a generic fashion.
For some reason, Statics must derive from object or you get a compile error. In this case, deriving from a different base class does increase code re-useability for me. Is there another way to accomplish my goal?
I believe the only choice I have is to make my static class an instance class.
//a data container used for Mocking in inversion of control
public class FieldContainer : ReflectionHelper<FieldContainer>
{
public static string Field1 = "Some Data";
public static string Field2 = "Persons Name";
public static string Field3 = "3030 Plane Ave.";
}
public class ReflectionHelper<T>
{
public static string ToCSV()
{
StringBuilder fieldCollector = new StringBuilder();
Type type = typeof(T);
FieldInfo[] fields = type.GetFields();
foreach (FieldInfo f in fields)
{
fieldCollector.Append(f.GetValue(null) + ",");
}
return fieldCollector.ToString();
}
}
Your code is perfectly valid (at least technically). Your class FieldContainer is not a static class and therefore it can derive from ReflectionHelper<T>.
However, you normally would not implement the method ToCSV in a base class, because it can basically work on ANY class. Because you want to work on static members, an extension method isn't the best way either. The simplest and cleanest way to do it, will be to have a static helper class that implements this method:
public static class ReflectionHelper
{
public static string ToCSV<T>()
{
StringBuilder fieldCollector = new StringBuilder();
Type type = typeof(T);
FieldInfo[] fields = type.GetFields();
foreach (FieldInfo f in fields)
{
fieldCollector.Append(f.GetValue(null) + ",");
}
return fieldCollector.ToString();
}
}
You can use it like this:
var csv = ReflectionHelper.ToCSV<FieldContainer>();
However, I fail to see, why you would want to implement something like that at all. It doesn't seem to make too much sense.
You could form it as an extension method, as such:
public static class ReflectionHelperExtensions
{
public static string ToCSV<T>(this T instance)
{
var type = instance.GetType();
var fields = type.GetFields();
var fieldCollector = new StringBuilder();
foreach (FieldInfo f in fields)
{
fieldCollector.Append(f.GetValue(null) + ",");
}
return fieldCollector.ToString();
}
}
This way, your field container classes don't need to derive from any given type, as this applies to all derivatives of object.
Have you concidered using an extension method?
public static class ReflectionExtensions
{
public static string ToCSV(this object input)
{
StringBuilder fieldCollector = new StringBuilder();
Type type = input.GetType();
FieldInfo[] fields = type.GetFields();
foreach (FieldInfo f in fields)
{
fieldCollector.Append(f.GetValue(null) + ",");
}
return fieldCollector.ToString();
}
}
Then you could simply call the following on any object:
FieldContainer c = new FieldContainer();
string result = c.ToCSV();
It's fine if you have the class as an instanced type.
public abstract class ReflectionHelper<T>
{
protected ReflectionHelper()
{ }
public static string ToCsv(string delimiter = ",")
{
var fieldCollector = new StringBuilder();
var type = typeof(T);
var fields = type.GetFields();
foreach (var f in fields)
{
fieldCollector.Append(f.GetValue(null) + delimiter);
}
return fieldCollector.ToString();
}
}
public class Something : ReflectionHelper<Something>
{
protected Something() : base()
{
}
public static string Field1 = "Some Data";
public static string Field2 = "Persons Name";
public static string Field3 = "3030 Plane Ave.";
}

How to convert variable name to string in c#.net? [duplicate]

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

Categories