Get all fields from static classes inside static class with reflection - c#

I have a static class that contains a lot of static classes. Each inner static class contains fields. I want to get all fields of all inner static classes.
public static class MyClass
{
public static class MyInnerClass1
{
public const string Field1 = "abc";
public const string Field2 = "def";
public const string Field3 = "ghi";
}
public static class MyInnerClass2
{
public const int Field1 = 1;
public const int Field2 = 2;
public const int Field3 = 3;
}
...
}
I would like to print out the name of each inner class followed by the name and value of each field.
For example:
MyInnerClass
Field1 = "abc"
...
I have no problem with getting the name of all the classes:
var members = typeof(MyClass).GetMembers(BindingFlags.Public | BindingFlags.Static);
var str = "";
foreach (var member in members)
{
str += member.Name +" ";
}
Or the name and value of all fields in a specific class:
var fields = typeof(MyClass.MyInnerClass1).GetFields();
foreach (var field in fields)
{
str += field.Name + "-";
str += field.GetValue(typeof(MyClass.MyInnerClass1));
}
But how do I combine this?
The names and the number of inner static classes may change.

Try the following
var builder = new StringBuilder();
foreach (var type in typeof(MyClass).GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic))
{
if (!type.IsAbstract)
{
continue;
}
builder.AppendLine(type.Name);
foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)) {
var msg = String.Format("{0} = {1}", field.Name, field.GetValue(null));
builder.AppendLine(msg);
}
}
string output = builder.ToString();
The use of !type.IsAbstract is done to weed on non-static nested types. A static type in C# is generated as abstract under the hood.
Also my solution will pick up both public and non-public members (both types and fields). I'm not sure if this was your intent or not so you may want to modify that part of my solution.

You need to recursively loop through type.GetNestedTypes():
IEnumerable<FieldInfo> GetAllFields(Type type) {
return type.GetNestedTypes().SelectMany(GetAllFields)
.Concat(type.GetFields());
}

Related

Failing to get fields of an object

Simple c# console application to check how to get fields of an unknown object.
public class A
{
public int id;
public string name;
public string desc;
}
class Program
{
static void Main(string[] args)
{
A a = new A();
getProp(a);
Console.ReadKey();
}
static object getProp(object o)
{
Type type = o.GetType();
PropertyInfo[] pros = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
//Do Something
return null;
}
}
I am not getting any fields. pros has no value inside it. I have to get the field names of the object o.
The members which you're trying to fetch are not properties, but fields. Try the following:
var fields = typeof(A).GetFields();
Or:
static FieldInfo[] GetFields(object o)
{
Type type = o.GetType();
FieldInfo[] fields = type.GetFields();
return fields;
}
And in order to grab the object's fields values:
var fields = GetFields(obj);
foreach(var field in fields)
{
Console.WriteLine(field.GetValue(obj));
}
From MSDN:
Type.GetFields Method
Returns all the public fields of the current Type.

Convert a list of a custom class to an object array

I have a list of a custom class, which contains various variables of type double, string, etc.
In order to return this to excel with Excel DNA, I need to convert the list to object[,].
Here is what I did, but is there a better way? If I add another variable I need to remember to redo the number of columns, etc.
List<customclass> BMrep = somefunction();
var retdata = new object[BMrep.Count,22];
for (int i = 0; i < BMrep.Count; i++)
{
retdata[i, 0] = BMrep[i].product_code;
retdata[i, 1] = BMrep[i].RG;
...
}
return retdata;
You could use reflection to get the properties:
var properties = typeof(customclass).GetProperties(BindingFlags.Public |
BindingFlags.Instance).OrderBy(x => x.Name).ToList();
List<customclass> BMrep = somefunction();
var retdata = new object[BMrep.Count, properties.Count];
for (int i = 0; i < BMrep.Count; i++)
{
for (int j = 0; j < properties.Count; j++)
{
retdata[i, j] = properties[j].GetValue(BMrep[i], null);
}
}
return retdata;
Well, Tim S. gave you a pretty solid answer.
However, if those various variables you mentioned are fields and not properties, it won't work. You could just change it to GetFields instead of GetProperties (and GetValue wouldn't need the second null parameter)
Here's another example which would work with fields (using linq, a bit shorter):
class A
{
public string Field1;
public string Field2;
public string Field3;
}
static void Main(string[] args)
{
A myA = new A() { Field1 = "rofl", Field2 = "lol", Field3 = "omg" };
var obj = (from prop in typeof(A).GetFields(BindingFlags.Public | BindingFlags.Instance)
select prop.GetValue(myA)).ToArray();
Debug.Assert(obj[0] == "rofl");
Debug.Assert(obj[1] == "lol");
Debug.Assert(obj[2] == "omg");
}
If you'd like it to work with properties, just replace the linq with this:
var obj2 = (from prop in typeof(A).GetProperties(BindingFlags.Public | BindingFlags.Instance)
select prop.GetValue(myA,null)).ToArray();
I typed var to be short but obj and obj2 are of type Object[]
EDIT:
As specified by MSDN,
The GetProperties method does not return properties in a particular order, such as alphabetical or declaration order.
So if you want to keep the order, you could create an Attribute on the field/property which could keep this information. See below:
class OrderAttribute : Attribute
{
public int Order { get; private set; }
public OrderAttribute(int order)
{
this.Order = order;
}
}
class A
{
public string FieldWithoutOrder;
[Order(3)] public string Field1;
[Order(2)] public string Field2;
[Order(1)] public string Field3;
}
static void Main(string[] args)
{
A myA = new A() { Field1 = "rofl", Field2 = "lol", Field3 = "omg", FieldWithoutOrder = "anarchy" };
var obj = (from prop in typeof(A).GetFields(BindingFlags.Public | BindingFlags.Instance)
orderby prop.GetCustomAttributes(typeof(OrderAttribute),true).Select(att=>((OrderAttribute)att).Order).DefaultIfEmpty(Int32.MaxValue).First()
select prop.GetValue(myA)
).ToArray();
var obj2 = (from prop in typeof(A).GetProperties(BindingFlags.Public | BindingFlags.Instance)
select prop.GetValue(myA,null)).ToArray();
Debug.Assert(obj[0] == "omg"); //Field3
Debug.Assert(obj[1] == "lol"); //Field2
Debug.Assert(obj[2] == "rofl"); //Field1
Debug.Assert(obj[3] == "anarchy"); //FieldWithoutOrder
}
The DefaultIfEmpty specifies a value in case the field doesn't have the Order Attribute.
This way it'll be easy for you to maintain (if you need to change the order of the fields, just change in the attribute)
I didn't do it for obj2 but you can paste the same orderby statement and it'll work.
Hope it helps.

C# use string as a class field name

Sorry if the tile is misleading. What i would like to do is to use a string to get the values from a class. What i have:
class foo
{
public string field1 {get;set;}
public string field2 {get;set;}
}
public void run()
{
//Get all fields in class
List<string> AllRecordFields = new List<string>();
Type t = typeof(foo);
foreach (MemberInfo m in t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
AllRecordFields.Add(m.Name);
}
foo f = new foo();
foreach(var field in AllRecordFields)
{
//field is a string with the name of the real field in class
f.field = "foobar";
}
}
This a really simple example, so the problem is on the line f.field = "foobar";
The field is a string with a name of the real class field what i want to assignt the value to.
Use PropertyInfo instead of MemberInfo and then SetValue.
public void run()
{
foo f = new foo();
Type t = typeof(foo);
foreach (PropertyInfo info in t.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
info.SetValue(f, "foobar", new object[0]);
}
}
First of all, it is better to use Properties instead of fields.
Second your fields are private, can't be accessed from outside foo. You need to declare them as public.
For your example you have to use reflection to access those files. But that is slow and it is not very good style. You better use the class directly (with property setter) or us an interface.
Add method into foo class to change all properties
class foo
{
public string field1 {get;set;}
public string field2 { get; set; }
public void SetValueForAllString( string value)
{
var vProperties = this.GetType().GetProperties();
foreach (var vPropertie in vProperties)
{
if (vPropertie.CanWrite
&& vPropertie.PropertyType.IsPublic
&& vPropertie.PropertyType == typeof(String))
{
vPropertie.SetValue(this, value, null);
}
}
}
}
foo f = new foo() { field1 = "field1", field2 = "field2" };
f.SetValueForAllString("foobar");
var field1Value = f.field1; //"foobar"
var field2Value = f.field2; //"foobar"

Getting all variables inside a struct of unknown size (C#)

I have a project in which I have to get all the variables from a struct in a different class and add all their names to a combo box in a form. My primary issue at the moment is iterating through the struct in a way that I can add each variable individually to the combo box. I tried doing this:
msgVars vars = new msgVars();
foreach (object obj in vars)
{
comboBox1.Items.Add(GetName(obj));
}
but as you probably know, you can't iterate through a struct that way. Is there any way I can do this without hardcoding the variables in?
Also, for reference, this is the GetName function:
static string GetName<T>(T item) where T : struct
{
var properties = typeof(T).GetProperties();
if (properties.Length == 1)
{
return properties[0].Name;
}
else
{
return null;
}
}
If you are looking for a way to access properties of a struct (or class) at runtime without predefining in code that set of properties, what you probably need to use is reflection.
Here's an example:
struct MyStruct
{
private readonly int m_Age;
private readonly string m_LastName;
private readonly string m_FirstName;
public int Age { get { return m_Age; } }
public string LastName { get { return m_LastName; } }
public string FirstName { get { return m_FirstName; } }
public MyStruct( int age, string last, string first )
{
m_Age = age;
m_LastName = last;
m_FirstName = first;
}
}
class StructReflection
{
public static void Main(string[] args)
{
var theStruct = new MyStruct( 40, "Smith", "John" );
PrintStructProperties( theStruct );
}
public static void PrintStructProperties( MyStruct s )
{
// NOTE: This code will not handle indexer properties...
var publicProperties = s.GetType().GetProperties();
foreach (var prop in publicProperties)
Console.WriteLine( "{0} = {1}", prop.Name, prop.GetValue( s, null ) );
}
}
You could try using Reflection. How about storing the information in a Hashtable.
public Hashtable GetPropertyInfo(Person person)
{
var properties = new Hashtable();
PropertyInfo[] propInfo = person.GetType().GetProperties();
foreach (PropertyInfo prop in propInfo)
{
properties.Add(prop.Name, prop.GetValue(person, null));
}
return properties;
}
Then you could write the information out via:
var person = new Person()
Person.Name = "Test";
Person.Age = 21;
var PropertyInfo = GetPropertyInfo(person);
foreach (string key in PropertyInfo.Keys)
{
Console.WriteLine("{0} = {1}", key, PropertyInfo[key]);
}
A struct is a single entity, not a collection of variables. This means that you can't 'iterate' over its properties. What you need to do is get a collection of the property names and iterate over it. Your GetName function can't do this because it just returns the name of the first property.
To add the property names to a combo all you have to do is:
var vars = new msgVars();
foreach(var name in GetNames(vars))
comboBox1.Items.Add(name);
In fact, getting the property name is so easy that you could get rid of GetNames completley and just write
foreach (var prop in typeof(msgVars).GetProperties())
comboBox1.Items.Add(prop.Name);
There are various ways you can write GetNames to return a collection of names. You can fill a List with the property names although the simplest is to have it return an iterator like this:
public static IEnumerable<string> GetNames<T>(T obj) where T:struct
{
var properties = typeof (T).GetProperties();
foreach (var propertyInfo in properties)
{
yield return propertyInfo.Name;
}
}
Finally, you don't really need to pass an instance of your struct to the method, as you are enumerating the structs property names, not their values. You can rewrite GetNames like this
public static IEnumerable<string> GetNames<T>() where T:struct
{
var properties = typeof (T).GetProperties();
foreach (var propertyInfo in properties)
{
yield return propertyInfo.Name;
}
}
and load the names like this
foreach(var name in GetNames<msgVars>())
comboBox1.Items.Add(name);

How to create a method that takes any object and returns a name:value pair for all properties?

For example I have a simple class like
public class Person{
public int Age {get;set;}
public string Name {get;set;}
}
I need to make a method that takes any class and spits out values of properties set in the object as a string in format "Age:35;Name:John Doe;"
I am looking for a method signature on the lines of
public string SpitNameValuePairs<T>()
or something like it. How can this be done efficiently if using reflection?
Here is a quick implementation.
public static string SplitNameValuePairs(object value)
{
string retVal = string.Empty;
List<string> keyValuePairs = new List<string>();
foreach (var propInfo in value.GetType().GetProperties())
{
keyValuePairs.Add(string.Format("{0}:{1};", propInfo.Name, propInfo.GetValue(value, null).ToString()));
}
retVal = string.Join("", keyValuePairs.ToArray());
return retVal;
}
Then called like this:
var person = new Person();
person.Name = "Hello";
person.Age = 10;
Console.WriteLine(SplitNameValuePairs(person));
That have protection from crashes when property have an indexer defined and also it output only instance properties (no static).
private static string SplitNameValuePairs<T>(T value)
{
StringBuilder sb = new StringBuilder();
foreach (PropertyInfo property in typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
if (property.GetIndexParameters().Length == 0)
sb.AppendFormat("{0}:{1};", property.Name, property.GetValue(value, null));
}
return sb.ToString();
}

Categories

Resources