Passing a property into a method to change that property - c#

Not sure if this is possible, but here is what I am trying to do:
I want to have a dictionary that contains a mapping of a column index to a property name used to populate that index.
In my code I will loop through an array if strings and use the dictionary to look up which column it should map to.
My end result code would look like:
for(int index = 0; index < fields.Length)
{
fieldPropertyMapping[index] = StripQuotes(fields[index]);
}

To do what you're asking specifically, you'll have to use reflection (as you tagged your question) to do this. Have a look at the PropertyInfo class. I'm not entirely certain what your code is doing, but a general example of reflectively setting a property value would be:
object targetInstance = ...; // your target instance
PropertyInfo prop = targetInstance.GetType().GetProperty(propertyName);
prop.SetValue(targetInstance, null, newValue);
You could, however, pass an Action<T> instead, if you know the property at some point in the code. For example:
YourType targetInstance = ...;
Action<PropertyType> prop = value => targetInstance.PropertyName = value;
... // in your consuming code
prop(newValue);
Or, if you know the type when you call it but you don't have the instance, you could make it an Action<YourType, PropertyType>. This also would prevent creating a closure.
Action<YourType, PropertyType> prop = (instance, value) => instance.PropertyName = value;
... // in your consuming code
prop(instance, newValue);
To make this fully generic ("generic" as in "non-specific", not as in generics), you'll probably have to make it an Action<object> and cast it to the proper property type within the lambda, but this should work either way.

You have a couple of choices:
Use reflection. Store and pass a PropertyInfo object into the method and set it's value through reflection.
Create an ActionDelegate with a closure to that property and pass that into the method.

you can use reflection to get the properties for a class:
var properties = obj.GetType().GetProperties();
foreach (var property in properties)
{
//read / write the property, here... do whatever you need
}

Related

How to put all delegate methods into a dictionary?

I am working on a caching solution to get all property fields of a class, create getter and setter delegates out of them, and store them.
This is how I do it at the moment:
// entity is a class with a single public property called 'Name'
var entity = new Entity("Cat");
var nameProp = typeof(Entity)
.GetProperties()
.Single(x => x.Name == nameof(Entity.Name));
// getting getter and setter methods
var namePropGetter = nameProp.GetGetMethod();
var namePropSetter = nameProp.GetSetMethod();
// creating delegates for getter and setter methods
var namePropGetterDelegate = (Func<Entity, string>)Delegate.CreateDelegate(typeof(Func<Entity, string>), namePropGetter);
var namePropSetterDelegate = (Action<Entity, string>)Delegate.CreateDelegate(typeof(Action<Entity, string>), namePropSetter);
All getters are to be stored in one Dictionary and all setters are to be stored in another. This would let me quickly get or set values of an object rather than using a traditional PropertyField.GetValue() or PropertyField.SetValue().
The only problem is the storage of getter and setter delegates.
I tried storing all getters into Func<TargetClass, object> and setters into Action<TargetClass, object> like so:
var getterDelegate = (Func<Entity, object>)Delegate.CreateDelegate(typeof(Func<Entity, object>), namePropGetter);
var setterDelegate = (Action<Entity, object>)Delegate.CreateDelegate(typeof(Action<Entity, object>), namePropSetter);
But it would give me the following error:
Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.
Storing everything inside of Delegate would force me to use DynamicInvoke(), which defeats the purpose of caching everything in the first place because it's slow.
The error happens because you pass an incorrect type to Delegate.CreateDelegate. For instance, for the getter of an Int32 property you should pass Func<Entity, Int32> and not Func<Entity, object>. This can be achieved with typeof(Func<,>).MakeGenericType(Entity, propType).
If you want to have a dictionary of delegates per property name, you should use Dictionary<string, Delegate>, then cast Delegate to a specific delegate type before invoking it:
var gettersDictionary = new Dictionary<string, Delegate>();
var settersDictionary = new Dictionary<string, Delegate>();
foreach (var prop in typeof(Entity).GetProperties())
{
var getterDelegate = Delegate.CreateDelegate(
typeof(Func<,>).MakeGenericType(typeof(Entity), prop.PropertyType),
prop.GetGetMethod());
gettersDictionary.Add(prop.Name, getterDelegate);
var setterDelegate = Delegate.CreateDelegate(
typeof(Action<,>).MakeGenericType(typeof(Entity), prop.PropertyType),
prop.GetSetMethod());
settersDictionary.Add(prop.Name, setterDelegate);
}
T GetPropValue<T>(Entity entity, string name)
{
var getter = (Func<Entity, T>) gettersDictionary[name];
return getter(entity);
}
void SetPropValue<T>(Entity entity, string name, T value)
{
var setter = (Action<Entity, T>) settersDictionary[name];
setter(entity, value);
}
I don't think you need to mess with Delegate at all. You can store a collection of Delegate, but that doesn't do any good if you have to somehow know the "real" type of each delegate so you can cast it. That tends to run into a wall. It doesn't make sense to store a collection of some common type if, in order to use each one, you have to already know its underlying type. (If we're just going to cast to the expected type we could use Dictionary<string, object>.)
A dictionary of getters with the property name as the key would look like this:
var getters = Dictionary<string, Func<Entity, object>>();
To store the getter for a property (PropertyInfo discovered by reflection)
getters.Add(property.Name, (entity) => property.GetValue(entity));
Storing setters is more complicated. You could store them like this:
var setters = new Dictionary<string, Action<Entity, object>>();
...and add a property:
setters.Add(property.Name, (entity, value) => property.SetValue(entity, value);
But suppose you have an entity, a property name, and a value you want to set on that property. You can retrieve the Action from the dictionary, but without some other controls it's not certain that the value you intend to set is the right type. We're really back to the same problem. We're storing items as a "least common denominator" type in a collection, but in order to use them we have to know the real type. The benefit of polymorphism is when the common denominator is all we need to know.
Maybe you've already got it determined how you would keep that second part straight, but there's a good chance that once you have the dictionary you'll have a difficult time using what's in it. In that case it might be necessary to take a step back and see if there's another way to solve the larger problem that doesn't involve this approach.

Getting a PropertyInfo of a dynamic object

I have a library that is doing a bunch of reflection work relying on the PropertyInfo of the classes it receives (to get and set values).
Now I want to be able to work with dynamic objects, but I can't find how to get the PropertyInfo of a dynamic's properties. I've checked the alternatives, but for those I'd need to change everywhere I use PropertyInfo to get/set values.
dynamic entity = new ExpandoObject();
entity.MyID = 1;
// - Always null
PropertyInfo p = entity.GetType().GetProperty("MyID");
// - Always null
PropertyInfo[] ps = entity.GetType().GetProperties();
// - These are called everywhere in the code
object value = p.GetValue(entity);
p.SetValue(entity, value);
Is it possible to get or create a PropertyInfo somehow just to be able to use it's GetValue() and SetValue() on a dynamic object?
Under the covers an ExpandoObject is really just a dictionary. You can get at the dictionary just by casting it.
dynamic entity = new ExpandoObject();
entity.MyID = 1;
if(entity.GetType() == typeof(ExpandoObject))
{
Console.WriteLine("I'm dynamic, use the dictionary");
var dictionary = (IDictionary<string, object>)entity;
}
else
{
Console.WriteLine("Not dynamic, use reflection");
}
You could modify your Mapping method to check if the object being passed in is dynamic and route through a different path that just iterates over the keys of the dictionary.
https://dotnetfiddle.net/VQQZdy

Object does not match target type PropertyInfo SetValue - one class to another

So I have 2 classes, both have identical Property names. One class contains different variables: int, strings, bool and DateTime The second class contains only 1 int and the rest are all strings.
Now I want to loop through all the properties, get the value from class1, encrypt that data and save it as a string in obj2, then return it to the main form (to save it in a database later).
public PersoonEncrypted EncryptPersonClass(Class1 object1)
{
PersoonEncrypted persEncrypt = new PersoonEncrypted(); //second class obj
Type type = object1.GetType();
PropertyInfo[] properties = type.GetProperties();
Type type2 = persEncrypt.GetType();
PropertyInfo[] properties2 = type.GetProperties();
foreach (var bothProperties in properties.Zip(properties2, (obj1, obj2) => new { Obj1 = obj1, Obj2 = obj2 }))
{
string value = "";
value = bothProperties.Obj1.GetValue(object1) as string;
if (!string.IsNullOrWhiteSpace(value))
{
string encryptValue = Encrypt(value);
if ((bothProperties.Obj2 != null) && (bothProperties.Obj2.PropertyType == typeof(string)))
{ //!= null check has no effect at all
bothProperties.Obj2.SetValue(persEncrypt, encryptValue, null); //errorLine
}
}
}
return persEncrypt;
}
That is what I came up with until now.
I have, of course, searched for other solutions like this one. This, after applying some own changes, didn't return any errors, but it didn't save any encrypted strings into the class persEncrypt. What I concluded was, from that test, is that it was testing if the value in the second class(persEncrypt in my example) from the particular property was null, while it shouldn't do that, it should make a new instance of that variable and save it in the object class, but removing that check gave me the same error.
you're just .Zip-ing the two lists of PropertyInfo objects, which simply iterates through both lists and doesn't check or sort for any sort of matching. This could result in erroneous behavior depending on the order in which properties appear - consider using a .Join instead to match property names.
This code doesn't check for an indexer on the property before attempting to assign to it without one - any indexed property which is of type string will make it to this point and then throw an exception when you try to set it.
Because this code is calling into Properties, there's the possibility an exception is being thrown by the code of the Property itself. This is where a StackTrace from your exception could reveal much more about what's happening.
Your code also checks for a property of type string directly - when using reflection you should use IsAssignableFrom instead in order to allow for inherited types, though that is unlikely the issue in this one case.

Get PropertyInfo value

I'm trying to get the value from a PropertyInfo[], but I can't get it to work:
foreach (var propertyInfo in foo.GetType().GetProperties())
{
var value = propertyInfo.GetValue(this, null);
}
Exception: Object does not match target type.
Isn't this how it's supposed to be done?
You're trying to get properties from this when you originally fetched the PropertyInfos from foo.GetType(). So this would be more appropriate:
var value = propertyInfo.GetValue(foo, null);
That's assuming you want to effectively get foo.SomeProperty etc.
You're getting that exception because this isn't the same type as foo.
You should make sure you're getting the properties for the same object that you're going to try to get the value from. I'm guessing from your code that you're expecting this to be foo inside the scope of the loop (which isn't the case at all), so you need to change the offending line to:
var value = propertyInfo.GetValue(foo, null);
You are processing properties declared in foo's type, but try to read their values from this, which apparently isn't of the same type.

How do I write a method that can output data from any class using generics?

I'm trying to write a custom method to populate a ListView control using Generics:
private void BindDataToListView(List<T> containerItems)
{
this.View = View.Details;
this.GridLines = true;
this.FullRowSelect = true;
if (this.Items.Count > 0)
this.Items.Clear();
this.BeginUpdate();
int i = 0;
foreach (T item in containerItems)
{
// do something
}
this.EndUpdate();
}
The parameter containerItems can have many items since I'm using generics. But I get stuck in the foreach loop. How do I access the values in containerItems?
Do I have to use reflection on each instance of T in the foreach loop? I think I do to retrieve the property name. But once I have the property name of the type T, how do I retrieve the value?
The most common way of doing this (with winforms) is via TypeDescriptor; this allow you to use things DataTable the same as classes; the "full" pattern is quite complex (and involves checking for IListSource, ITypedList, etc; however, the short version is; to get the available properties:
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));
To get a named property:
PropertDescriptor prop = props[propName];
To get a value for an instance (sourceObject):
object val = prop.GetValue(sourceObject);
To render a value as a string (using the designated converter):
string s = prop.Converter.ConvertToString(val);
You could limit T to an interface, and use that interface in the iteration.
What does T represent ?
Like it is now, it is a generic type and it can be ... anything.
So, what I would do, is create an interface IListViewBindable or something like that. That interface could then have a method 'CreateListViewItem' for instance.
Then, I would change the method, so that a constraint is applied to your type-parameter T, saying that T should implement IListViewBindable, like this:
public void BindDataToListView<T>( List<T> containerItems ) where T : IListViewBindable
{}
In your BindDataToListView method, you could then do this:
foreach( T item in containerItems )
{
this.Items.Add (item.CreateListViewItem());
}
If the items in the list are of totally unconstrained type, then you can treat them as simply of type object. You call GetType() to get the type of the object. On that you can call GetProperties() to get an array of PropertyInfo objects. And on those you can call GetValue() to retrieve the value of the property.
If you already know the name of a property, just call GetProperty() to retrieve it:
string valueAsString = item.GetType().GetProperty("Something")
.GetValue(item, null).ToString();
I don't completely understand what you're asking, but I think that this will point you in the right direction. Please ask for clarification if it looks like it can help and it's unclear.
You can access a given property of an object using reflection via
object o;
PropertyInfo info = o.GetType().GetProperty().GetProperty("NameOfPropertyIWant");
and you can get the value via
object value = info.GetValue(o, null);
Now, if you're going to be accessing a property of the same name on objects of various types, you should consider adding an interface
public interface IHasThePropertyIWant {
object NameOfPropertyIWant { get; }
}
Then you can enforce this via
void BindDataToListView(List<T> containerItems) where T : IHasThePropertyIWant
and use it in the look like so
foreach (T item in containerItems) {
object o = item.NameOfPropertyIWant;
// do something with o
}
ObjectListView uses reflection to do exactly what you are trying to do: populate a ListView using reflection. You could save yourself a lot of trouble by using it. It has already solved all the tricky problems you are going to encounter on this path.
If you really, really want to do it yourself, Marc's answer is (of course) completely correct.

Categories

Resources