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"
Related
I am at a complete loss here, despite looking at multiple SO posts and anything else I can think of.
My goal here is to make a really, really simple mapper. Something I can basically use as a tool in some unit tests. It doesn't need to be sophisticated or anything -- just map high-level primitive and string values of one object to another. So the basic algorithm is:
Get all properties from TFrom
Get all properties from TTo
Get all properties that are in both, matched by name.
I know this could be a bug in that they could have the same name but a different type, but let's set that aside. It's not what I'm running into here -- the properties and types match between classes.
Create an instance of TTo that we can copy to.
For each property that was mapped between the objects:
Get the value off of the from object
Convert the value to the type of the property
Set the value on the to object
The problem is that no matter what I do, and no matter what the type of the property is (int or string, for example) I get the following:
Object does not match the target type.
Here is the code I'm using:
public TTo Map<TFrom, TTo>(TFrom from)
{
if (from == null) return default;
var fromProps = GetProperties(typeof(TFrom));
var toProps = GetProperties(typeof(TTo));
// Props that can be mapped from one to the other
var propsToCopy = fromProps.Intersect(toProps, new PropertyComparer()).ToList();
var returnObject = (TTo)Activator.CreateInstance(typeof(TTo));
foreach (var prop in propsToCopy)
{
// Copy the values
var fromValue = prop.GetValue(from, null);
var convertedValue = Convert.ChangeType(fromValue, prop.PropertyType);
prop.SetValue(returnObject, convertedValue, null);
}
return returnObject;
}
public PropertyInfo[] GetProperties(Type objectType)
{
var allProps = objectType.GetProperties(
BindingFlags.Public | BindingFlags.Instance);
return allProps.Where(p => p.PropertyType.IsPrimitive ||
p.PropertyType == typeof(string)).ToArray();
}
private class PropertyComparer : IEqualityComparer<PropertyInfo>
{
public bool Equals(PropertyInfo x, PropertyInfo y)
{
return x.Name.Equals(y.Name);
}
public int GetHashCode(PropertyInfo obj)
{
return obj.Name.GetHashCode();
}
}
And here's an example of a way I would call it, with sample classes:
public class Foo
{
public string StringProp { get; set; }
public int IntProp { get; set; }
}
public class FooOther
{
public string StringProp { get; set; }
public int IntProp { get; set; }
}
var foo = new Foo { IntProp = 1, StringProp = "foo" };
var mappedFoo = Map<Foo, FooOther>(foo);
About the only hint I've gotten out of Visual Studio is from the watch window: if the property type is a string, the watch window reports the type of convertedValue as object. If the property type is an int, the watch window reports object {int}.
The PropertyInfo you are using is still coupled to the type the property it is representing is a member of, so you aren't able to use it to set the value of an object of another type without the error you are getting.
Here's a shortened example of the behavior:
public class A {
public string Id {get;set;}
}
public class B {
public string Id {get;set;}
}
void Main()
{
var test = new A() { Id = "Test"};
var prop = test.GetType().GetProperty("Id");
var b = (B)Activator.CreateInstance(typeof(B));
var fromValue = prop.GetValue(test);
var converted = Convert.ChangeType(fromValue, prop.PropertyType);
prop.SetValue(b, converted, null); // Exception
}
This makes sense if you think of the PropertyInfo as a member of A. To fix this, you'll want to get a property info that's specific to your type. I can fix up my example with the following:
var propTo = typeof(B).GetProperty(prop.Name);
propTo.SetValue(b, converted, null);
Console.WriteLine(b.Id); // Output: Test
Bringing that together, if you change the contents of your foreach to the following you should be in the clear:
foreach (var prop in propsToCopy)
{
// Copy the values
var fromValue = prop.GetValue(from, null);
var convertedValue = Convert.ChangeType(fromValue, prop.PropertyType);
var propTo = typeof(TTO).GetProperty(prop.Name);
propTo.SetValue(returnObject, convertedValue, null);
}
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.
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());
}
I'm trying to clone instances of a derived class, but somehow it doesn't work well. The cloning method is:
public static T CloneFieldsAndProperties<T>(T input)
{
T result = (T)Activator.CreateInstance(typeof(T));
PropertyInfo[] listOfProps = typeof(T).GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.CreateInstance);
FieldInfo[] listOfFields = typeof(T).GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.CreateInstance);
foreach (PropertyInfo prop in listOfProps) prop.SetValue(result, prop.GetValue(input, null), null);
foreach (FieldInfo field in listOfFields) field.SetValue(result, field.GetValue(input));
return result;
}
As you can see, I added many BindingFlags because it wasn't working. But to no avail.
It does work in a simple case:
MyclassA1 a1 = new MyclassA1();
MyclassA a = CloneFieldsAndProperties(a1);
if (a is MyclassA1) Text = "Works";
Where:
class MyclassA
{
public int i;
}
class MyclassA1 : MyclassA
{
public int i1;
}
But in my real program it doesn't. The real program's classes' declarations are long so I'm not posting them here. What might be the problem?
I had the same issue a long time ago. The only real solution for me, after lots of googling, was to serialize and deserialize it. It's not a bad solution and you lose only a little bit of performance, just do it like this:
Add this tag to your class:
[Serializable()]
public class a
{
}
And then you can create a function like this:
public object Clone()
{
IO.MemoryStream mem = new IO.MemoryStream();
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter form = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
form.Serialize(mem, this);
mem.Position = 0;
return form.Deserialize(mem);
}
If you need a shallow clone, simply use Object.MemberwiseClone. If you need a deep clone, serialize and then deserialize your object (e.g. using BinaryFormatter or DataContractSerializer). This would take care of issues such as cycles and cross references.
This will work and may be faster than the serialization method:
Code:
using System;
namespace Cloning
{
class Program
{
static void Main(string[] args)
{
Derived d = new Derived() { property = 1, field = 2, derivedProperty = 3, derivedField = 4 };
Base b = new Derived(d);
// Change things in the derived class.
d.property = 5;
d.field = 6;
d.derivedProperty = 7;
d.derivedField = 8;
// Output the copy so you know it's not shallow.
Console.WriteLine((b as Derived).property);
Console.WriteLine((b as Derived).field);
Console.WriteLine((b as Derived).derivedProperty);
Console.WriteLine((b as Derived).derivedField);
Console.ReadLine();
}
class Base
{
public int property { get; set; }
public int field;
}
class Derived : Base
{
public Derived() { }
public Derived(Derived d)
{
// Perform the deep copy here.
// Using reflection should work, but explicitly stating them definitely
// will, and it's not like it's not all known until runtime.
this.property = d.property;
this.field = d.field;
this.derivedProperty = d.derivedProperty;
this.derivedField = d.derivedField;
}
public int derivedProperty { get; set; }
public int derivedField;
}
}
}
Test:
http://goo.gl/pQnAL
Output:
1
2
3
4
Comments:
I would really imagine that this would work in more than just a trivial case but perhaps not:
public static T CloneFieldsAndProperties<T>(T input)
{
T result = (T)Activator.CreateInstance(input.GetType());
PropertyInfo[] properties = input.GetType().GetProperties();
FieldInfo[] fields = input.GetType().GetFields();
foreach (PropertyInfo property in properties)
property.SetValue(result, property.GetValue(input, null), null);
foreach (FieldInfo field in fields)
field.SetValue(result, field.GetValue(input));
return result;
}
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();
}