I´ve been searching and trying for some hours now but it doesn´t make sense to me right now.
Basically I have two class libraries in a .net 4.5 project.
On using reflection I´m able to access some properties but not all and I d not see my fault.
Library A defines the datastructure:
public class HeaderRow
{
public string Format { get; set; }
public Int32 Version { get; set; } = 0;
public CustomEnum Datacategory { get; set; }
public Custom2Enum FormatnameEnum { get; set; }
public int Formatversion { get; set; }
public UInt64 CreatedAt { get; set; }
public string Origin {get; set;}
}
which I´d like to fill in library 2 with this code:
protected PropertyInfo FindPropertyInfo(object o, string propertyName)
{
Type objektTyp = o.GetType();
PropertyInfo info = objektTyp.GetProperty(propertyName,BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
return info;
}
public override void FillValueToColumns(object o, string property, string value)
{
FindPropertyInfo(o,property).SetValue(o,value);
}
I have access to:
Format but not Origin
CreatedAt but not Formatversion
and I do not see my custom Enums.
For all those I do not see "info" is null.
Any help is apreciated.
Thanks
You will not be able to use SetValue to assign a string to, for example, an int. To do that you will either need overloads of FillValueToColumns that take the int (for example) as the last parameter, or else you need to get a type converter, like this:
PropertyInfo propertyInfo = FindPropertyInfo( o, propertyName);
TypeConverter typeConverter = TypeDescriptor.GetConverter( propertyInfo.PropertyType);
object v = typeConverter.ConvertFrom( null, CultureInfo.CurrentCulture, value ?? "");
if (v == null && propertyInfo.PropertyType.IsValueType && Nullable.GetUnderlyingType(propertyInfo.PropertyType) == null) {
throw new InvalidCastException("Assignment of null to a non-nullable value type!");
}
propertyInfo.SetValue( object, v);
sjb
Kudos to you. My bad. Sorry.
Indeed a naming issue.
By copy & paste I created blanks in my string properties. So I was searching for
"Origin "
and not
"Origin"
-> Deleting surplus blanks solved the issue.
Related
I'm trying to set default property values for certain classes. Because the classes are generated automatically, I don't want to have to set them in the individual classes, so I created an extension method. It works great until I come across nullable properties, such as bool? properties because PropertyInfo.PropertyType will not return bool. In fact, it's unclear to me what to test for when PropertyType comes across a nullable bool.
Here's the outline of my method:
public static T SetDefaults<T>(this T model) where T : IModelClasses
{
//set values
foreach (PropertyInfo prop in model.GetType().GetProperties())
{
if (prop.PropertyType == typeof(string)) prop.SetValue(model, string.Empty, null);
else if (prop.PropertyType == typeof(bool)) prop.SetValue(model, false, null); //Will not set the property to false if the property is a nullable bool
...
}
return model;
}
I've looked into ways of getting the underlying type, such as below but the original object is required. Because PorpertyType does not return the actual property, I cannot test it using this method:
private static Type GetType<T>(T obj)
{
return typeof(T);
}
Is there any way to evaluate the PropertyInfo or ProperType to determine if it is a bool? so that I can set it to false?
Updated Answer
I needed similar functionality for one of my projects so I posted the code to GitHub and NuGet for reference. It use dynamic code gen and Roslyn compilation for performance.
If you paste the code below into LINQPad and install the NuGet package Initialize, it should work. If it throw an error about extension methods, just copy the following code from the repo into your project - Initializer.cs, InitializerTemplate.cs, InitializerExtensions.cs
void Main()
{
//Install-Package Initialize
//Github code url: https://github.com/adam-dot-cohen/initialize
var test = new Test();
var test2 = new Test2();
// INITIALIZER EXAMPLE
test.Dump("Test Pre Init");
// 1.Optional If you want to manipuate the default initialization logic.
Initializer<Test>.Template.Clear();
Initializer<Test>.Template.Add(typeof(string),
(obj, propInfo) => "string.Empty");
Initializer<Test>.Template.Add(typeof(Nullable<>),
(obj, propInfo) => string.Format("{0}.{1}!.InitValueOrDefault()", obj, propInfo.Name));
Initializer<Test>.Template.Add(typeof(ValueType),
(obj, propInfo) => string.Format("{0}.{1}.InitValueOrDefault()", obj, propInfo.Name));
// 2. Call initialize
Initializer<Test>.Initialize(test);
test.Dump("Test Post Init");
}
public class Test2
{
public int Prop { get; set; }
public int? PropNullable { get; set; }
public string PropString { get; set; }
public int? FieldNullable { get; set; }
}
public class Test
{
public int Prop { get; set; }
public int? PropNullable { get; set; }
public string PropString { get; set; }
public int? FieldNullable { get; set; }
}
I have a little problem, below my code:
public class A
{
public string ObjectA { get; set; }
}
public void Run()
{
A a = new A();
a.ObjectA = "Test number 1";
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
PropertyInfo myPropertyInfo = a.GetType().GetProperty("ObjectA", flags);
object myNewObject = myPropertyInfo.GetValue(a);// Here should be reference
a.ObjectA = "Test number 2";//After this line value myNewObject continued "Test number 1"
}
So my value myNewObject must be in output "Test number 2". Is there any way? It is this at all possible?
Wrong!
You're getting the string rather than the instance of A using reflection.
Changing A.ObjectA doesn't change the string reference. Actually, you're setting a different string to the backing string class field by the ObjectA property...
Auto-properties are syntactic sugar to avoid explicitly declaring class fields to properties which perform nothing when getting or setting their values:
// For example:
public string Text { get; set; }
// is...
private string _text;
public string Text { get { return _text; } set { _text = value; } }
Now turn your code into regular one (no reflection):
A a = new A();
a.ObjectA = "hello world";
object myNewObject = a.ObjectA;
// Do you think now that myNewObject should change...? :)
a.ObjectA = "goodbye";
Is there any way? It is this at all possible?
No.
Maybe you can simulate this behavior using a Func<T>...
Func<object> myNewObjectGetter = () => myPropertyInfo.GetValue(a);
Now, whenever you call myNewObjectGetter() you're going to get the most fresh value of the whole property. BTW, this still doesn't address the impossible!
Is there any way?
No. You can't put a reference to a property into an object variable. Such a variable can only hold a normal value, such as the string you put into it.
That answers the question as asked. You can clarify what you want to achieve and maybe we can suggest a better way.
Probably there is no solution but I show some code
public class MyRows
{
public string Key { get; set; }
public int Id { get; set; }
public object Val { get; set; }
}
public abstract class BasicDTO
{
public int? Id { get; private set; }
public PropertyInfo[] PropertyDTO;
protected Type myType;
public BasicDTO()
{
Load();
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
PropertyDTO = myType.GetProperties(flags);
}
}
public class CustomerDTO : BasicDTO
{
public string Surname { get; set; }
public string Phone { get; set; }
public CustomerDTO() { }
protected override void Load()
{
myType = typeof(CustomerDTO);
}
}
Now my basic method
public void Run(BasicDTO dto)
{
PropertyInfo pi = dto.PropertyDTO.Where(x => x.Name == "Surname").SingleOrDefault();
MyRows mr = new MyRows();
mr.Val = pi.GetValue(dto);//Here I need reference
}
When I change CustomerDTO.Surname my value mr.Val it must also be changed.
As I wrote above, it is probably impossible, but maybe anybody have a idea.
BTW: Value mr.Val I use only for binding (WPF). So maybe you have any other suggestions, how solve problem. I will be grateful for your help
Sorry if this is a duplicate. I searched and was not able to find an answer.
How do I use reflection to get Values of Properties of class at multilevel?
I have a List of string that has some string values like this:
ClassNames = {"FirstName", "LastName", "Location.City", "Location.State", "Location.Country", "PhoneNo"}
I have two classes
public class Details
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Location Location { get; set; }
public string PhoneNo { get; set; }
}
public class Location
{
public long City { get; set; }
public string State { get; set; }
public string Country { get; set; }
}
I used reflection and I am able to get the values of firstname, lastname and phone. But how do I get the values in the location class? It throws an Error. I change the List to just have Location / City . I am missing something. I dont want to do Multiple for loops as the level might drill down to n level. (4 max to be realistic)
foreach (string fieldname in ClassNames)
{
string fieldvalue = RestultDTO[indexValue]GetType().GetProperty(fieldname) ;
}
You'd have to get the Location instance first:
var s = "Location.City";
var sVals = s.Split('.');
// here [t] is the TypeInfo of [Details]
var l = t.GetProperty(sVals[0]);
^ gets the Location PropertyInfo (really only need to do this once
var val = l.GetProperty(sVals[1]).GetValue(l.GetValue(o));
^ gets the PropertyInfo for the property you're after (City in this case)
^ gets the actual value of that property
^ gets the value of Location for o where o is an instance of Details
If you're using a version before 4.5 you'll probably need to send in one additional parameter to the GetValue method - it can be , null both times because the properties aren't indexers.
Here are two methods I wrote to solve this issue
This one gets a queue of properties from a base object:
private static Queue<PropertyInfo> GetProperty(object obj, string path)
{
Queue<PropertyInfo> values = new Queue<PropertyInfo>();
Type object_type = obj.GetType();
string[] properties = path.Split('.');
PropertyInfo propertyInfo = null;
foreach (string property in properties)
{
if (propertyInfo != null)
{
Type propertyType = propertyInfo.PropertyType;
propertyInfo = propertyType.GetProperty(property);
values.Enqueue(propertyInfo);
}
else
{
propertyInfo = object_type.GetProperty(property);
values.Enqueue(propertyInfo);
}
}
return values;
}
And this one uses the queue and the object to get the actual values:
private static T GetValue<T>(object obj, Queue<PropertyInfo> values)
{
object location = obj;
while (values.Count > 0)
{
location = values.Dequeue().GetValue(location);
}
return (T)location;
}
Probably wouldn't hurt to add some error checking and such as well.
My problem is quite simple
Suppose I have those class:
public class A
{
public Collection<B> B { get; set; }
public Collection<C> C { get; set; }
}
public class B
{
public int IntB { get; set; }
}
public class C
{
public string StringC { get; set; }
}
And I write a function:
public void GetValue(string fieldName){
A a = new A();
PropertyInfo infor = typeof(A).GetProperty(fieldName);
object obj = infor.GetValue(a,null);
}
My question is how can I turn obj to corresponding Collection, in this case is Collection<B> or Collection<C>, depending in fieldName value
Thank in advance
You can cast it:
var collection = (Collection<B>)(infor.GetValue(a,null));
EDIT:
if you use LINQ on the resulting collections, you may want to use OfType (link: http://msdn.microsoft.com/en-us/library/bb360913.aspx) and/or Cast (link: http://msdn.microsoft.com/en-us/library/bb341406.aspx)
Like
var collection = getCollection(a,null));
collection.OfType<B>.Select(b => b.IntB)....
You can't have a statically typed object in your method when you determine the property that will be called at runtime. But the runtime time of obj is of the actual type of your property.
Take this class for example:
public class Applicant : UniClass<Applicant>
{
[Key]
public int Id { get; set; }
[Field("X.838.APP.SSN")]
public string SSN { get; set; }
[Field("APP.SORT.LAST.NAME")]
public string FirstName { get; set; }
[Field("APP.SORT.FIRST.NAME")]
public string LastName { get; set; }
[Field("X.838.APP.MOST.RECENT.APPL")]
public int MostRecentApplicationId { get; set; }
}
How would I go about getting all of the properties that are decorated with the field attribute, get their types, and then assign a value to them?
This is all done with reflection. Once you have a Type object, you can then get its PropertyInfo with myType.GetProperties(), from there, you can get each property's attributes with GetCustomAttributes(), and from there if you find your attribute, you've got a winner, and then you can proceed to work with it as you please.
You already have the PropertyInfo object, so you can assign to it with PropertyInfo.SetValue(object target, object value, object[] index)
You'll need to use Reflection:
var props =
from prop in typeof(Applicant).GetProperties()
select new {
Property = prop,
Attrs = prop.GetCustomAttributes(typeof(FieldAttribute), false).Cast<FieldAttribute>()
} into propAndAttr
where propAndAttr.Attrs.Any()
select propAndAttr;
You can then iterate through this query to set the values:
foreach (var prop in props) {
var propType = prop.Property.PropertyType;
var valueToSet = GetAValueToSet(); // here's where you do whatever you need to do to determine the value that gets set
prop.Property.SetValue(applicantInstance, valueToSet, null);
}
You would just need to invoke the appropriate reflection methods - try this:
<MyApplicationInstance>.GetType().GetProperties().Where(x => x.GetCustomAttributes().Where(y => (y as FieldAttribute) != null).Count() > 0);