views:

690

answers:

4
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.

+3  A: 

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());
}
Elisha
thanks... but my problem now is how to get the value foreach (var item in typeProperties){Attributes.Add(item.Name, item.*can't get the value here*);}
Jronny
Replaced the usage example to something similar that fills the attributes in the dictionary :)
Elisha
Thank you. I'll give it a try later.
Jronny
Thanks a lot for this, but I have just figured that we dont actually need nullable properties, but instead, the properties whose names are included on a **list of strings** defined. This would mean another `where` clause, but I am not familiar with lambda expressions. Help please. Thanks.
Jronny
GetType().GetProperties().Where(property => propertiesNames.Contains(property.Name))
Elisha
thanks. It's okay now. I have found a different way however, but this is good.
Jronny
+2  A: 

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:

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

Maslow
I might need to use this someday.
Jronny
which one is that? I gave 4 possible solutions/ideas
Maslow
Information caching... But the others could also be useful someday. Thanks.
Jronny
+1  A: 

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());
}
Jason
+1  A: 

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();
    }
}
Dr. Wily's Apprentice