I've a class "TradingStrategy", with n subclasses ("Strategy1, Strategy2 etc...").
I've a simple UI from which i can choose a subclass (I've got all the subclasses of the "TradingStrategy" class pretty easily).
What i want now is to print (in a datagridview, listbox, combobox, doesn't matter) all the public parameters of the choosen subclass.
I would prefer not to instantiate the subclasses.
namespace BackTester
{
class TradingStrategy
{
public string Name;
}
class MA_Test : TradingStrategy
{
new public string Name = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name;
public int len = 12;
public float lots = 0.1F;
public bool trendFollow = true;
public MA_Test()
{
}
}
class MA_Test2 : TradingStrategy
{
new public string Name = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name;
public int len = 24;
public float lots = 0.1F;
public bool trendFollow = true;
public MA_Test2()
{
}
}
}
With this code i can insert into a combo box every subclass of "TradingStrategy"
var type = typeof(TradingStrategy);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p));
foreach (var t in types){
if (t.Name == "TradingStrategy") continue;
boxStrategy.Items.Add(t.Name);
}
I wanna be able to, from the combobox.Text, get all the properties name and values of the corrisponding subclass.
I think I've read (and tried) every post here and in other forum. Many use reflections.
What is the simplest way to get those prop/values?
Thanks
Why not just create an interface ITradingStrategy:
public interface ITradingStrategy
{
string Name { get; }
int len { get; }
float lots { get; }
bool trendFollow { get; }
}
And have all classes inherit from the interface then pull values from interface.
As was mentioned in the comments, you have to instantiate an instance of the class in order to set some values on it.
To get the public fields/properties and their types without instantiating the objects, you can use reflection as follows:
private static Dictionary<string, Type> GetFields(Type t)
{
var fields = new Dictionary<string, Type>();
foreach (var memberInfo in t.GetMembers(BindingFlags.Instance | BindingFlags.Public))
{
var propertyInfo = memberInfo as PropertyInfo;
var fieldInfo = memberInfo as FieldInfo;
if (propertyInfo != null)
{
fields.Add(propertyInfo.Name, propertyInfo.PropertyType);
}
if (fieldInfo != null)
{
fields.Add(fieldInfo.Name, fieldInfo.FieldType);
}
}
return fields;
}
If you already have the object, you can get all the public fields/values with this method.
private static Dictionary<string, object> GetValues(FileInfo o)
{
var values = new Dictionary<string, object>();
foreach (var memberInfo in o.GetType().GetMembers(BindingFlags.Instance | BindingFlags.Public))
{
var propertyInfo = memberInfo as PropertyInfo;
var fieldInfo = memberInfo as FieldInfo;
if (propertyInfo != null)
{
values.Add(propertyInfo.Name, propertyInfo.GetValue(o, null));
}
if (fieldInfo != null)
{
values.Add(fieldInfo.Name, fieldInfo.GetValue(o));
}
}
return values;
}
The following code is a very slow way to get all the types which derive from a given type, due to the way that the CLR implements GetTypes() and the fact there could be thousands of unrelated types in your code which makes the haystack to search even bigger. The only time you should use this method is if you dynamically load assemblies at runtime containing object definitions that you need to load. Unfortunately there is no other way to get this information at runtime:
var type = typeof(TradingStrategy);
var subtypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => p != type && type.IsAssignableFrom(p));
I would recommend that you store this list of types somewhere in your code, e.g. in an array, and iterate over it when you need to know all of your strategies:
private static readonly Type[] TradingStrategies =
{
typeof(Strategy1),
typeof(Strategy2),
typeof(Strategy3),
};
After reading Erik's answer. If you will never instantiate these classes, you could store this data in a configuration file, and use something like JSON.net to read it, or if you don't want to use an external library, XmlSerializer would work as well. In this case you would store each MATest as a Dictionary (which lends itself nicely to JSON.net's JObject. Using JSON.net, you would have a configuration file that looks like:
[
{
"MA_Test": {
"len": 12,
"lots": 0.1,
"trendFollow": true
},
"MA_Test2": {
"len": 24,
"lots": 0.1,
"trendFollow": true
}
}
]
Then read it with code that looks like:
public JObject ReadConfig(string configPath)
{
using (var filestream = File.Open(configPath, FileMode.Open))
using (var streamReader = new StreamReader(filestream))
using (var jsonTextReader = new JsonTextReader(streamReader))
{
var jsonSerializer = new JsonSerializer();
return jsonSerializer.Deserialize<JObject>(jsonTextReader);
}
}
Thank you all for you answers.
The simplest way I found to get the properties from an indirected instantiated class is this:
var strategy = activator.CreateInstance(Type.GetType("BackTester."+boxStrategy.Text));
foreach (FieldInfo prop in strategy.GetType().GetFields(BindingFlags.Public
| BindingFlags.Instance))
{
listBox1.Items.Add(prop.ToString() + " " + prop.GetValue(strategy));
}
Based on the code you've provided, there is no reason for there to be separate classes for each MA_Test (X DO NOT use underscores, hyphens, or any other nonalphanumeric characters.). Instead these should be the same class with different properties (not fields).
class TradingStrategy
{
public string Name { get; set; }
}
class MATest : TradingStrategy
{
// this is not needed if it is inherited by TradingStragegy
// You should be getting a warning that you are hiding
// the field/property
// public string Name { get; set; }
// Should probably be more descriptive
// e.g. LengthInFeet...
public int Length { get; set; }
public float Lots { get; set; }
// I recommended boolean properties to be prefixed with
// Is, Can, Has, etc
public bool CanTrendFollow { get; set; }
}
// Somewhere Else...
var MATests = new List<MATest>()
{
new MATest()
{
Name = "MATest",
Length = 12,
Lots = 0.1F,
CanTrendFollow = true
},
new MATest()
{
Name = "MATest",
Length = 24,
Lots = 0.1F,
CanTrendFollow = true
},
}
Now instead of costly Reflection and Activator, just create the list classes once (manually, from config or even a database), and they can be used for whatever you need.
Related
I will try to explain my best, but it is a tricky one to explain.
I am having a problem using reflection when a derived object redefines a property already in a base class.
Let's consider the following classes to start with:
// The base class
namespace MyNamesapce
{
public abstract class MyClassBase: IMyClassBase
{
[JsonConstructor]
public MyClassBase()
{
}
public string Info { get; set; }
public string Unit { get; set; }
public string Name { get; set; }
}
}
// MyClassArray is most of the time used
namespace MyNamesapce
{
public class MyClassArray<TType> : MyClassBase, IMyClassArray<TType>
{
public MyClassArray()
{
}
[JsonConstructor]
public MyClassArray(IEnumerable<TType> value, TType minValue, TType maxValue)
{
MinValue = minValue;
MaxValue = maxValue;
Value = value;
}
public IEnumerable<TType> Value { get; set; }
public TType MinValue { get; set; }
public TType MaxValue { get; set; }
}
}
// In some rare cases we need 2D arrays
namespace MyNamesapce
{
public class MyClass2DArray<TType> : MyClassArray<TType>, IMyClass2DArray<TType>
{
private int[] _arraySize { get; set; }
public MyClass2DArray()
{
}
[JsonConstructor]
public MyClass2DArray(TType[][] value, TType minValue, TType maxValue)
{
MinValue = minValue;
MaxValue = maxValue;
_arraySize = new int[2] { value.Count(), value[0].Length };
Value = value;
}
public new TType[][] Value
{
get
{
TType[][] array2D = new TType[_arraySize[0]][];
// Reconstruct the 2D array
TType[] tmpArray;
int startIdx = 0;
for (int numArrays = 0; numArrays < _arraySize[0]; numArrays++)
{
tmpArray = new TType[_arraySize[1]];
Array.Copy(base.Value.ToArray(), startIdx, tmpArray, 0, _arraySize[1]);
startIdx += _arraySize[1];
array2D[numArrays] = tmpArray;
}
return array2D;
}
set
{
// Should not be able to set _value to null
if (value == null)
return;
base.Value = value.SelectMany(v => v).ToArray();
}
}
}
}
I now need to get all the properties from all instances of MyClassArray and MyClassArray2D. You will say, there are plenty of threads discussing that very point, just use "GetType().GetProperties()" for the former and use "GetType().GetProperty(..., BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)" for the latter.
The problem is that I do not know in advance which class is being processed. In my system when deserialising a Json, instances of both MyClassArray and MyClassArray2D have to be reconstructed, which is done using the following setter:
public static void SetProperty(this Object obj, string propName, Object value)
{
PropertyInfo info = null;
object[] indexer = null;
string[] nameParts = propName.Split('.');
if (obj == null) { return; }
var props = obj.GetType().GetProperties();
for (int idx = 0; idx < nameParts.Count() - 1; idx++)
{
try
{
indexer = null;
// Try to access normal property
info = obj.GetType().GetProperty(nameParts[idx]);
if (info == null)
continue;
obj = info.GetValue(obj, indexer);
}
catch
{
info = null;
indexer = null;
}
}
if (obj != null)
{
// !!! Note that here, using declare only will only work when using derived classes
PropertyInfo propertyToSet = obj.GetType().GetProperty(nameParts.Last(), BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly); // | BindingFlags.DeclaredOnly);
propertyToSet?.SetValue(obj, value);
}
else
{
throw new SystemException($"Could not find the property {propName}");
}
}
As you can see an object is passed in to SetProperty() (that can be of any type).
When it is of type MyClassArray, there are no problems, but if it is of type MyClassArray2D it does not quite work as the latter redefines "Value", which will break the logic as 2 properties called value will exist. I need a way to detect that.
The first loop seems to do the right thing. "obj = info.GetValue(obj, indexer);" will return "obj" containing all the versions of "Value". The problem is in the next part of SetProperty().
How can I detect when more than one "Value" property is in "obj"? And how to always pick the derived version of "Value"?
Also if I just use "BindingFlags.DeclaredOnly" as done here in my code snipet, properties from the base class get lost/disappear, which is undesirable.
Is there maybe a way to return in "obj" all the properties without the duplicates coming from the base class? Or some kind of property filter maybe?
I need help to solve a problem, my problem is as follows, I have the following object
public class Teste
{
public string Descricao { get; set; }
public Time Time { get; set; }
}
.
public class Time
{
public string Nome { get; set; }
public Time (string nome)
{
Nome = nome;
}
}
I would like to be able to obtain the complete path of a certain property.
var teste = new Teste();
teste.Descricao = "bar";
teste.Time = new Time("foo");
var b = GetProperties(teste, "Nome");
//expected return: "Time.Nome"
I was testing something I arrived at the following method
public static IEnumerable<Tuple<string, string>> GetProperties(object obj, string propertyPath)
{
var objType = obj.GetType();
if (objType.IsValueType || objType.Equals(typeof(string)))
return Enumerable.Repeat(Tuple.Create(propertyPath, obj.ToString()), 1);
else
{
if (obj == null)
return Enumerable.Repeat(Tuple.Create(propertyPath, string.Empty), 1);
else
{
return from prop in objType.GetProperties()
where prop.CanRead && !prop.GetIndexParameters().Any()
let propValue = prop.GetValue(obj, null)
let propType = prop.PropertyType
from nameValPair in GetProperties(propValue, string.Format("{0}.{1}", propertyPath, prop.Name))
select nameValPair;
}
}
}
but it returns everything to me and I would like it to return a specific property.
I think there are some issues with searching properties that come from system modules. You have to decide which properties are worth recursively descending and which ones are not. Also, you'll have to maintain a list of objects that you have already visited to ensure that you do not follow cycles. I think a breadth-first search would be best, but for this example, I'll code a depth-first search. Also, I just return the first match, not all matches, you can adjust as needed. Furthermore, it returns a (mostly useless) string version of the path rather than a list of reflected properties that would be needed to actually access it (You'd have to do reflection again to locate the properties by name to retrieve the value from this "path" string.)
I'll start you off with a basic implementation. Likely someone else can improve upon it.
static string GetPropertyPath(object obj, string name, List<object> visited = null)
{
// does the object have the property?
Type t = obj.GetType();
var properties = t.GetProperties();
foreach (var property in properties) {
if (property.Name == name) {
// that's it!
return name;
}
}
// if we get here, it's because we didn't find the property.
if (visited == null) {
visited = new List<object>();
visited.Add(obj);
}
// Get all the properties of the first object and keep searching,
// keeping track of objects we've visited already.
foreach (var property in properties) {
// Limit which kinds of properties we search
if (object.ReferenceEquals(typeof(Program).Module, property.Module)) {
// get the value of the property
object obj2 = property.GetValue(obj);
// Do not search any previously visited objects
if (!visited.Any(o => object.ReferenceEquals(o, obj2))) {
visited.Add(obj2);
string path = GetPropertyPath(obj2, name, visited);
if (path != null) {
// found it!
return property.Name + "." + path;
}
}
}
}
return null;
}
Example
static void Main(string[] args)
{
var teste = new Teste();
teste.Descricao = "bar";
teste.Time = new Time("foo");
var b = GetPropertyPath(teste, "Nome"); // "Time.Nome"
}
I have a class which normally contains Fields, Properties. What i want to achieve is instead of this:
class Example
{
public string Field = "EN";
public string Name { get; set; }
public int? Age { get; set; }
public List<string> A_State_of_String { get; set; }
}
public static void Test()
{
var c1 = new Example
{
Name = "Philip",
Age = null,
A_State_of_String = new List<string>
{
"Some Strings"
}
};
var c2 = new Example();
//Instead of doing that
c2.Name = string.IsNullOrEmpty(c1.Name) ? "" : c1.Name;
c2.Age = c1.Age ?? 0;
c2.A_State_of_String = c1.A_State_of_String ?? new List<string>();
//Just do that
c1.CopyEmAll(c2);
}
What i came up with but doesn't work as expected.
public static void CopyEmAll(this object src, object dest)
{
if (src == null) {
throw new ArgumentNullException("src");
}
foreach (PropertyDescriptor item in TypeDescriptor.GetProperties(src)) {
var val = item.GetValue(src);
if (val == null) {
continue;
}
item.SetValue(dest, val);
}
}
Problems:
Although i checked for null, it seems to bypass it.
Doesn't seem to copy Fields.
Notes:
I don't want to use AutoMapper for some technical issues.
I want the method to copy values and not creating new object. [just mimic the behavior i stated in the example]
I want the function to be recursive [if the class contains another classes it copies its values too going to the most inner one]
Don't want to copy null or empty values unless i allow it to.
Copies all Fields, Properties, or even Events.
Based on Leo's answer, but using Generics and copying also the fields:
public void CopyAll<T>(T source, T target)
{
var type = typeof(T);
foreach (var sourceProperty in type.GetProperties())
{
var targetProperty = type.GetProperty(sourceProperty.Name);
targetProperty.SetValue(target, sourceProperty.GetValue(source, null), null);
}
foreach (var sourceField in type.GetFields())
{
var targetField = type.GetField(sourceField.Name);
targetField.SetValue(target, sourceField.GetValue(source));
}
}
And then just:
CopyAll(f1, f2);
You can use serialization to serialize object A and deserialize as object B - if they have very same structure, you can look here for object deep copy.
Deep cloning objects
I know you don't want to use Automapper, but if the types have only SIMILAR structure, you should maybe use Automapper which is based on reflection. You can download a nuget and find some information here:
https://www.nuget.org/packages/AutoMapper/
your code then will look like
public TOutput CopyAll<TInput, TOutput>(TInput input)
{
var config = new MapperConfiguration(cfg => cfg.CreateMap<TInput, TOutput>());
IMapper mapper = config.CreateMapper();
return mapper.Map<TOutput>(vstup);
}
I'm writing my own method to convert an object graph to a custom object since the JavaScriptSerializer fires errors on null values.
So this is what I have so far:
internal static T ParseObjectGraph<T>(Dictionary<string, object> oGraph)
{
T generic = (T)Activator.CreateInstance<T>();
Type resType = typeof(T);
foreach (PropertyInfo pi in resType.GetProperties())
{
object outObj = new object();
if (oGraph.TryGetValue(pi.Name.ToLower(), out outObj))
{
Type outType = outObj.GetType();
if (outType == pi.PropertyType)
{
pi.SetValue(generic, outObj, null);
}
}
}
return generic;
}
Now the pi.SetValue() method runs, and doesn't fire an error but when I look at the properties of generic, it's still the same as it was before hand.
The first property it goes through is a boolean so the values end up like this
generic = an object of type MyCustomType
generic.property = false
outObj = true
pi = boolean property
outType = boolean
Then after the SetValue method runs, generic.property is still set to false.
PropertyInfo.SetValue/GetValue worked with struct with accurate using
struct Z
{
public int X { get; set; }
}
Z z1 = new Z();
z1.GetType().GetProperty("X").SetValue(z1, 100, null);
Console.WriteLine(z1.X); //z1.X dont changed
object z2 = new Z();
z2.GetType().GetProperty("X").SetValue(z2, 100, null);
Console.WriteLine(((Z)z2).X); //z2.x changed to 100
Z z3 = new Z();
object _z3 = z3;
_z3.GetType().GetProperty("X").SetValue(_z3, 100, null);
z3 = (Z)_z3;
Console.WriteLine(z3.X); //z3.x changed to 100
Correct way to change struct:
box struct
change property of boxed struct
assign boxed struct to source
Found the answer. Apparently, PropertyInfo.SetValue() and PropertyInfo.GetValue() do not work for structures, only classes.
MyCustomType was unfortunately a struct, so changing this to a class made it work.
The 3rd reply in this thread states why structures do not work and classes do.
EDIT: It does work with structs, see the marked answer.
So I took your method and made a unit test of it:
class PropertySetTest
{
static readonly Type resType = typeof(Car);
internal static T ParseObjectGraph<T>(Dictionary<string, object> oGraph)
{
T generic = (T)Activator.CreateInstance<T>();
foreach (PropertyInfo pi in resType.GetProperties())
{
//No need to new() this
object outObj; // = new object();
if (oGraph.TryGetValue(pi.Name.ToLower(), out outObj))
{
Type outType = outObj.GetType();
if (outType == pi.PropertyType)
{
pi.SetValue(generic, outObj, null);
}
}
}
return generic;
}
[Test]
public void Test()
{
var typeData = new Dictionary<String, Object> {{"color", "Blue"}};
var myCar = ParseObjectGraph<Car>(typeData);
Assert.AreEqual("Blue", myCar.Color);
}
}
internal class Car
{
public String Color { get; set; }
}
This passes. Can you make it not pass in the way that you are seeing?
EDIT: With your struct, it is only marginally more complicated. See Jon Skeet's answer here regarding what's going on. As for the working code:
class PropertySetTest
{
static readonly Type resType = typeof(Car);
internal static T ParseObjectGraph<T>(Dictionary<string, object> oGraph)
{
Object generic = Activator.CreateInstance<T>();
foreach (var pi in resType.GetProperties())
{
//No need to new() this
object outObj; // = new object();
if (oGraph.TryGetValue(pi.Name.ToLower(), out outObj))
{
var outType = outObj.GetType();
if (outType == pi.PropertyType)
pi.SetValue(generic, outObj, null);
}
}
return (T)generic;
}
[Test]
public void Test()
{
var typeData = new Dictionary<String, Object> {{"color", "Blue"}};
var myCar = ParseObjectGraph<Car>(typeData);
Assert.AreEqual("Blue", myCar.Color);
}
}
internal struct Car
{
public String Color { get; set; }
}
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());
}