How to put all delegate methods into a dictionary? - c#

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.

Related

Initialise existing anonymous object type, via reflection

I'd like to manipulate some anonymous types, by inspecting property values, and replacing specific values with a new value in certain circumstances. The problem is, anonymous type properties are read only, i.e. they don't have a setter.
My plan is to treat the anonymous types like any other immutable object, and implement a visitor pattern to return a new instance where necessary, with new property values where required.
What I need to make this work, is a way to initialise a new instance of an anonymous type, dynamically, and set the property values.
Is there a way to dynamically call the initializer for a specific object type, via reflection?
Here's some code to give you an idea of what I'm doing...
var newResults = results.Select(r => VisitResult(r));
// recursive function that visits each property of our results, and manipulates the data
// as required
object VisitResult(object result)
{
// if the object is of our specific data type, we need to check if we need to replace it
if (result is IDataRow row)
{
// check if we should replace the value, and return the new value if we have one
return updatedValues.Lookup(row) ?? row;
}
else
{
// this doesn't work for anonymous types, as the properties are read only
// I'd like to declare a new instance of the same anonymous type, and use
// the initialiser, so I can assign new values to the anonymous type properties
foreach (var propertyInfo in result.GetType().GetProperties())
{
// visit the value of each property
propertyInfo.SetValue(row, VisitResult(propertyInfo.GetValue(row)));
}
}
}
If I cannot achieve this via reflection, I will use Expression trees instead, I was just curious if there was a way of using initialisers via reflection, as my Google-fu hasn't managed to turn up anything relevant.

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

C# reflection dictionary

say I have this code:
Dictionary<String, String> myDictionary = new Dictionary<String, String>();
Type[] arguments = myDictionary.GetType().GetGenericArguments();
In my program, myDictionary it's of unknown types (it's an object returned from a deserialized XML), but for the purpose of this question, they are string. I want to create something like this:
Dictionary<arguments[0],arguments[1]> mySecondDictionary = new Dictionary<arguments[0],arguments[1]>();
Obviously, it doesn't work.
I searched on MSDN, and I saw they are using the Activator class, but I don't get it.
Maybe somebody more advanced, could help me a little.
You can use the activator class like you mentioned in order to create objects from given types. The MakeGenericType method allows you to specify an array of Types as the parameters for generic objects, which is what you were trying to simulate.
Dictionary<String, String> myDictionary = new Dictionary<String, String>();
Type[] arguments = myDictionary.GetType().GetGenericArguments();
Type dictToCreate = typeof(Dictionary<,>).MakeGenericType(arguments);
var mySecondDictionary = Activator.CreateInstance(dictToCreate);
The code above is essentially pointless as you know that the dictionary is String,String beforehand but assuming you have a way of detecting the required types elsewhere during runtime, you can use the last two lines to instantiate a dictionary of that type.
There is a problem with this approach.
I will try my best to explain it.
I wrote a program which first serializes a class into XML, then deserializes it back.
Basically, the class it's a generic one, and it contains a List(the same type with the class).
So, the type of the class could be anything, starting from simple types, like string, int, etc to more complex classes, like for example a book class, or a person. After using the XmlSerializer.Deserialize method, and getting the object, I should use Reflection to reconstruct back the object, and access the list. And I can't do it that way.
So, if I have something like:
Type classToCreate = typeof(classToBeSerialized<>).MakeGenericType(arguments);
var reconstructedClass = Activator.CreateInstance(classToCreate);
where classToBeSerialized it's the supposed class(which has the list which I have spoken of), and returnedObject it's the object returned from XmlSerializer.Deserialize, I want to access the list like this:
((reconstructedClass)returnedObject).lista
Basically, I'm using reflection to cast the object to it's source.
I know this is an old thread, but I just needed something similar, and decided to show it, (you know for google).
this is basicly a rewrite of the answer by #user2536272
public object ConstructDictionary(Type KeyType, Type ValueType)
{
Type[] TemplateTypes = new Type[]{KeyType, ValueType};
Type DictionaryType = typeof(Dictionary<,>).MakeGenericType(TemplateTypes);
return Activator.CreateInstance(DictionaryType);
}
public void AddToDictionary(object DictionaryObject, object KeyObject, object ValueObject )
{
Type DictionaryType = DictionaryObject.GetType();
if (!(DictionaryType .IsGenericType && DictionaryType .GetGenericTypeDefinition() == typeof(Dictionary<,>)))
throw new Exception("sorry object is not a dictionary");
Type[] TemplateTypes = DictionaryType.GetGenericArguments();
var add = DictionaryType.GetMethod("Add", new[] { TemplateTypes[0], TemplateTypes[1] });
add.Invoke(DictionaryObject, new object[] { KeyObject, ValueObject });
}

How can I pass a property as a delegate?

This is a theoretical question, I've already got a solution to my problem that took me down a different path, but I think the question is still potentially interesting.
Can I pass object properties as delegates in the same way I can with methods? For instance:
Let's say I've got a data reader loaded up with data, and each field's value needs to be passed into properties of differing types having been checked for DBNull. If attempting to get a single field, I might write something like:
if(!rdr["field1"].Equals(DBNull.Value)) myClass.Property1 = rdr["field1"];
But if I've got say 100 fields, that becomes unwieldy very quickly. There's a couple of ways that a call to do this might look nice:
myClass.Property = GetDefaultOrValue<string>(rdr["field1"]); //Which incidentally is the route I took
Which might also look nice as an extension method:
myClass.Property = rdr["field1"].GetDefaultOrValue<string>();
Or:
SetPropertyFromDbValue<string>(myClass.Property1, rdr["field1"]); //Which is the one that I'm interested in on this theoretical level
In the second instance, the property would need to be passed as a delegate in order to set it.
So the question is in two parts:
Is this possible?
What would that look like?
[As this is only theoretical, answers in VB or C# are equally acceptable to me]
Edit: There's some slick answers here. Thanks all.
I like using expression trees to solve this problem. Whenever you have a method where you want to take a "property delegate", use the parameter type Expression<Func<T, TPropertyType>>. For example:
public void SetPropertyFromDbValue<T, TProperty>(
T obj,
Expression<Func<T, TProperty>> expression,
TProperty value
)
{
MemberExpression member = (MemberExpression)expression.Body;
PropertyInfo property = (PropertyInfo)member.Member;
property.SetValue(obj, value, null);
}
Nice thing about this is that the syntax looks the same for gets as well.
public TProperty GetPropertyFromDbValue<T, TProperty>(
T obj,
Expression<Func<T, TProperty>> expression
)
{
MemberExpression member = (MemberExpression)expression.Body;
PropertyInfo property = (PropertyInfo)member.Member;
return (TProperty)property.GetValue(obj, null);
}
Or, if you're feeling lazy:
public TProperty GetPropertyFromDbValue<T, TProperty>(
T obj,
Expression<Func<T, TProperty>> expression
)
{
return expression.Compile()(obj);
}
Invocation would look like:
SetPropertyFromDbValue(myClass, o => o.Property1, reader["field1"]);
GetPropertyFromDbValue(myClass, o => o.Property1);
(Adding a second answer because it's on a completely different approach)
To address your original problem, which is more about wanting a nice API for mapping named values in a datareader to properties on your object, consider System.ComponentModel.TypeDescriptor - an often overlooked alternative to doing reflective dirtywork yourself.
Here's a useful snippet:
var properties = TypeDescriptor.GetProperties(myObject)
.Cast<PropertyDescriptor>()
.ToDictionary(pr => pr.Name);
That creates a dictionary of the propertydescriptors of your object.
Now I can do this:
properties["Property1"].SetValue(myObject, rdr["item1"]);
PropertyDescriptor's SetValue method (unlike System.Reflection.PropertyInfo's equivalent) will do type conversion for you - parse strings as ints, and so on.
What's useful about this is one can imagine an attribute-driven approach to iterating through that properties collection (PropertyDescriptor has an Attributes property to allow you to get any custom attributes that were added to the property) figuring out which value in the datareader to use; or having a method that receives a dictionary of propertyname - columnname mappings which iterates through and performs all those sets for you.
I suspect an approach like this may give you the API shortcut you need in a way that lambda-expression reflective trickery - in this case - won't.
Ignoring whether this is useful in your specific circumstances (where I think the approach you've taken works just fine), your question is 'is there a way to convert a property into a delegate'.
Well, there kind of might be.
Every property actually (behind the scenes) consists of one or two methods - a set method and/or a get method. And you can - if you can get a hold of those methods - make delegates that wrap them.
For instance, once you've got hold of a System.Reflection.PropertyInfo object representing a property of type TProp on an object of type TObj, we can create an Action<TObj,TProp> (that is, a delegate that takes an object on which to set the property and a value to set it to) that wraps that setter method as follows:
Delegate.CreateDelegate(typeof (Action<TObj, TProp>), propertyInfo.GetSetMethod())
Or we can create an Action<TProp> that wraps the setter on a specific instance of TObj like this:
Delegate.CreateDelegate(typeof (Action<TProp>), instance, propertyInfo.GetSetMethod())
We can wrap that little lot up using a static reflection extension method:
public static Action<T> GetPropertySetter<TObject, T>(this TObject instance, Expression<Func<TObject, T>> propAccessExpression)
{
var memberExpression = propAccessExpression.Body as MemberExpression;
if (memberExpression == null) throw new ArgumentException("Lambda must be a simple property access", "propAccessExpression");
var accessedMember = memberExpression.Member as PropertyInfo;
if (accessedMember == null) throw new ArgumentException("Lambda must be a simple property access", "propAccessExpression");
var setter = accessedMember.GetSetMethod();
return (Action<T>) Delegate.CreateDelegate(typeof(Action<T>), instance, setter);
}
and now I can get a hold of a 'setter' delegate for a property on an object like this:
MyClass myObject = new MyClass();
Action<string> setter = myObject.GetPropertySetter(o => o.Property1);
That's strongly typed, based on the type of the property itself, so it's robust in the face of refactoring and compile-time typechecked.
Of course, in your case, you want to be able to set your property using a possibly-null object, so a strongly typed wrapper around the setter isn't the whole solution - but it does give you something to pass to your SetPropertyFromDbValue method.
No, there's nothing akin to method group conversions for properties. The best you can do is to use a lambda expression to form a Func<string> (for a getter) or an Action<string> (for a setter):
SetPropertyFromDbValue<string>(value => myClass.Property1 = value,
rdr["field1"]);
Worth mentioning you can do this with some reflection trickery..
something like...
public static void LoadFromReader<T>(this object source, SqlDataReader reader, string propertyName, string fieldName)
{
//Should check for nulls..
Type t = source.GetType();
PropertyInfo pi = t.GetProperty(propertyName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
object val = reader[fieldName];
if (val == DBNull.Value)
{
val = default(T);
}
//Try to change to same type as property...
val = Convert.ChangeType(val, pi.PropertyType);
pi.SetValue(source, val, null);
}
then
myClass.LoadFromReader<string>(reader,"Property1","field1");
As others have pointed, static reflexion is the way to go.
Those classes work out of the box :
http://www.codeproject.com/Articles/36262/Getting-Fun-with-Net-Static-Reflection.aspx

Passing a property into a method to change that property

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
}

Categories

Resources