Related
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.
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.
I'd like to access the value of a dynamic c# property with a string:
dynamic d = new { value1 = "some", value2 = "random", value3 = "value" };
How can I get the value of d.value2 ("random") if I only have "value2" as a string? In javascript, I could do d["value2"] to access the value ("random"), but I'm not sure how to do this with c# and reflection. The closest I've come is this:
d.GetType().GetProperty("value2") ... but I don't know how to get the actual value from that.
As always, thanks for your help!
Once you have your PropertyInfo (from GetProperty), you need to call GetValue and pass in the instance that you want to get the value from. In your case:
d.GetType().GetProperty("value2").GetValue(d, null);
public static object GetProperty(object target, string name)
{
var site = System.Runtime.CompilerServices.CallSite<Func<System.Runtime.CompilerServices.CallSite, object, object>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.GetMember(0, name, target.GetType(), new[]{Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(0,null)}));
return site.Target(site, target);
}
Add reference to Microsoft.CSharp. Works also for dynamic types and private properties and fields.
Edit: While this approach works, there is almost 20× faster method from the Microsoft.VisualBasic.dll assembly:
public static object GetProperty(object target, string name)
{
return Microsoft.VisualBasic.CompilerServices.Versioned.CallByName(target, name, CallType.Get);
}
Dynamitey is an open source .net std library, that let's you call it like the dynamic keyword, but using the a string for the property name rather than the compiler doing it for you, and it ends up being equal to reflection speedwise (which is not nearly as fast as using the dynamic keyword, but this is due to the extra overhead of caching dynamically, where the compiler caches statically).
Dynamic.InvokeGet(d,"value2");
The easiest method for obtaining both a setter and a getter for a property which works for any type including dynamic and ExpandoObject is to use FastMember which also happens to be the fastest method around (it uses Emit).
You can either get a TypeAccessor based on a given type or an ObjectAccessor based of an instance of a given type.
Example:
var staticData = new Test { Id = 1, Name = "France" };
var objAccessor = ObjectAccessor.Create(staticData);
objAccessor["Id"].Should().Be(1);
objAccessor["Name"].Should().Be("France");
var anonymous = new { Id = 2, Name = "Hilton" };
objAccessor = ObjectAccessor.Create(anonymous);
objAccessor["Id"].Should().Be(2);
objAccessor["Name"].Should().Be("Hilton");
dynamic expando = new ExpandoObject();
expando.Id = 3;
expando.Name = "Monica";
objAccessor = ObjectAccessor.Create(expando);
objAccessor["Id"].Should().Be(3);
objAccessor["Name"].Should().Be("Monica");
var typeAccessor = TypeAccessor.Create(staticData.GetType());
typeAccessor[staticData, "Id"].Should().Be(1);
typeAccessor[staticData, "Name"].Should().Be("France");
typeAccessor = TypeAccessor.Create(anonymous.GetType());
typeAccessor[anonymous, "Id"].Should().Be(2);
typeAccessor[anonymous, "Name"].Should().Be("Hilton");
typeAccessor = TypeAccessor.Create(expando.GetType());
((int)typeAccessor[expando, "Id"]).Should().Be(3);
((string)typeAccessor[expando, "Name"]).Should().Be("Monica");
Much of the time when you ask for a dynamic object, you get an ExpandoObject (not in the question's anonymous-but-statically-typed example above, but you mention JavaScript and my chosen JSON parser JsonFx, for one, generates ExpandoObjects).
If your dynamic is in fact an ExpandoObject, you can avoid reflection by casting it to IDictionary, as described at http://msdn.microsoft.com/en-gb/library/system.dynamic.expandoobject.aspx.
Once you've cast to IDictionary, you have access to useful methods like .Item and .ContainsKey
The GetProperty/GetValue does not work for Json data, it always generate a null exception, however, you may try this approach:
Serialize your object using JsonConvert:
var z = Newtonsoft.Json.JsonConvert.DeserializeObject(Convert.ToString(request));
Then access it directly casting it back to string:
var pn = (string)z["DynamicFieldName"];
It may work straight applying the Convert.ToString(request)["DynamicFieldName"], however I haven't tested.
d.GetType().GetProperty("value2")
returns a PropertyInfo object.
So then do
propertyInfo.GetValue(d)
To get properties from dynamic doc
when .GetType() returns null, try this:
var keyValuePairs = ((System.Collections.Generic.IDictionary<string, object>)doc);
var val = keyValuePairs["propertyName"].ToObject<YourModel>;
This is the way i ve got the value of a property value of a dinamic:
public dynamic Post(dynamic value)
{
try
{
if (value != null)
{
var valorCampos = "";
foreach (Newtonsoft.Json.Linq.JProperty item in value)
{
if (item.Name == "valorCampo")//property name
valorCampos = item.Value.ToString();
}
}
}
catch (Exception ex)
{
}
}
Some of the solutions were not working with a valuekind object that I obtained from a json string, maybe because I did not have a concrete type in my code that was similar to the object that I would obtain from the json string, so how I went about it was
JsonElement myObject = System.Text.Json.JsonSerializer.Deserialize<JsonElement>(jsonStringRepresentationOfMyObject);
/*In this case I know that there is a property with
the name Code, otherwise use TryGetProperty. This will
still return a JsonElement*/
JsonElement propertyCode = myObject.GetProperty("Code");
/*Now with the JsonElement that represents the property,
you can use several methods to retrieve the actual value,
in this case I know that the value in the property is a string,
so I use the GetString method on the object. If I knew the value
was a double, then I would use the GetDouble() method on the object*/
string code = propertyCode.GetString();
That worked for me
In .Net core 3.1 you can try like this
d?.value2 , d?.value3
Similar to the accepted answer, you can also try GetField instead of GetProperty.
d.GetType().GetField("value2").GetValue(d);
Depending on how the actual Type was implemented, this may work when GetProperty() doesn't and can even be faster.
In case you have a dynamic variable such as a DapperRow for example you can first build up an ExpandoObject, then cast the Expando into an IDictionary<string, object>. From then on, getting a value via the name of a property is possible.
Helper method ToExpandoObject:
public static ExpandoObject ToExpandoObject(object value)
{
IDictionary<string, object> dapperRowProperties = value as IDictionary<string, object>;
IDictionary<string, object> expando = new ExpandoObject();
if (dapperRowProperties == null)
{
return expando as ExpandoObject;
}
foreach (KeyValuePair<string, object> property in dapperRowProperties)
{
if (!expando.ContainsKey(property.Key))
{
expando.Add(property.Key, property.Value);
}
else
{
//prefix the colliding key with a random guid suffixed
expando.Add(property.Key + Guid.NewGuid().ToString("N"), property.Value);
}
}
return expando as ExpandoObject;
}
Sample usage, I have marked in bold the casting which gives us access in the example, I have marked the important bits with the ** letters:
using (var transactionScope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
foreach (var dynamicParametersForItem in dynamicParametersForItems)
{
var idsAfterInsertion = (await connection.QueryAsync<object>(sql, dynamicParametersForItem)).ToList();
if (idsAfterInsertion != null && idsAfterInsertion.Any())
{
**var idAfterInsertionDict = (IDictionary<string, object>) ToExpandoObject(idsAfterInsertion.First());**
string firstColumnKey = columnKeys.Select(c => c.Key).First();
**object idAfterInsertionValue = idAfterInsertionDict[firstColumnKey];**
addedIds.Add(idAfterInsertionValue); //we do not support compound keys, only items with one key column. Perhaps later versions will return multiple ids per inserted row for compound keys, this must be tested.
}
}
}
In my example, I look up a property value inside a dynamic object DapperRow and first convert the Dapper row into an ExpandoObject and cast it into a dictionary property bag as shown and mentioned in other answers here.
My sample code is the InsertMany method for Dapper extension I am working on, I wanted to grab hold of the multiple ids here after the batch insert.
Use dynamic with Newtonsoft.Json.JsonConvert.DeserializeObject:
// Get JSON string of object
var obj = new { value1 = "some", value2 = "random", value3 = "value" };
var jsonString = JsonConvert.SerializeObject(obj);
// Use dynamic with JsonConvert.DeserializeObject
dynamic d = JsonConvert.DeserializeObject(jsonString);
// output = "some"
Console.WriteLine(d["value1"]);
Sample:
https://dotnetfiddle.net/XGBLU1
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
}
considering that fairly static data should not be re-evaluated but cached instead, I wondered if it is possible to use Reflection to obtain class properties once, and then cache them so that I could dynamically evaluate object properties and read/assign values, but not have the Reflection overhead every time I do that. Is this possible (Sample code?) ?
To clarify a bit, lets say I have this class:
public class Cloud
{
Boolean IsWhite;
}
and I'm trying to now make a method that allows me to do something like this (pseudocode):
Update(myCloudInstance, new {IsWhite, true});
Update should now check with the cache first if it knows already the properties of Cloud (typeof(myCloudInstance)), and then use cached information to assign the property "IsWhite" the value "true" instead of doing Reflection again.
Any ideas on how to do this?
It's not clear exactly what you're doing, but caching can certainly make a difference with reflection.
In particular, if you're invoking methods (or property getters/setters) and can do so in a type-safe way as far as the calling code is concerned, it can make a huge difference if you convert the MethodInfo into a strongly-typed delegate once and then reuse that.
If you could give us a complete example of what you're trying to do, that would help us to come up with more specific ideas or even code. If you're just going to cache a PropertyInfo that may not have as much (or any) effect - it's possible that the normal Type.GetProperty (etc) methods are already pretty fast. As ever with performance questions, the key is to measure what you're actually doing. Make a change and measure again, etc.
The cost of reflection does not need to be as big as you think. In addition to delegates (that Jon discusses) you can also use things like HyperDescriptor to minimise the cost of reflection without changing the code much - it simply becomes PropertyDescriptor instead:
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(myCloudInstance);
// ideally cache props, but not essential
then
object val = props["IsWhite"].GetValue(myCloudInstance);
or if you use it lots, consider storing the PropertyDescriptor somewhere, too.
However... like Jon, I'm really not 100% sure what you're trying to do!
I created a hashtable to cache the reflection results. First time, it's neccessary to make a call to GetProperties and store the results into the hastable. Next times, first check the hashtable for the List of PropertyInfo objects. If exists, use it. If not, invoke GetProperties.
I use this to map a datareader to a List of Entities.
My implementation is based on: A Defense on Reflection in .Net, by Nick Harrison (http://www.simple-talk.com/dotnet/.net-framework/a-defense-of-reflection-in-.net/).
So, there it is:
public class MapeadorDataReaderListaObjetos
{
private Hashtable properties;
private Hashtable Properties
{
get
{
if (properties == null)
properties = new Hashtable();
return properties;
}
set { properties = value; }
}
private void LoadProperties(object targetObject, Type targetType)
{
var flags = BindingFlags.DeclaredOnly| BindingFlags.Instance| BindingFlags.Public;
if (properties == null)
{
List<PropertyInfo> propertyList = new List<PropertyInfo>();
PropertyInfo[] objectProperties = targetType.GetProperties(flags);
foreach (PropertyInfo currentProperty in objectProperties)
{
propertyList.Add(currentProperty);
}
properties = new Hashtable();
properties[targetType.FullName] = propertyList;
}
if (properties[targetType.FullName] == null)
{
List<PropertyInfo> propertyList = new List<PropertyInfo>();
PropertyInfo[] objectProperties = targetType.GetProperties(flags);
foreach (PropertyInfo currentProperty in objectProperties)
{
propertyList.Add(currentProperty);
}
properties[targetType.FullName] = propertyList;
}
}
public void MapearDataReaderListaObjetos <T> (IDataReader dr, List<T> lista) where T: new()
{
Type businessEntityType = typeof(T);
List<T> entitys = new List<T>();
T miObjeto = new T();
LoadProperties(miObjeto, businessEntityType);
List<PropertyInfo> sourcePoperties = Properties[businessEntityType.FullName] as List<PropertyInfo>;
while (dr.Read())
{
T newObject = new T();
for (int index = 0; index < dr.FieldCount; index++)
{
for (int _indice = 0; _indice < sourcePoperties.Count; _indice++)
{
if (sourcePoperties[_indice].Name.ToUpper() == dr.GetName(index).ToUpper());
{
string _tipoProp = sourcePoperties[_indice].PropertyType.ToString();
PropertyInfo info = sourcePoperties[_indice] as PropertyInfo;
if ((info != null) && info.CanWrite)
{
info.SetValue(newObject, dr.GetValue(index), null);
}
}
}
}
entitys.Add(newObject);
}
dr.Close();
lista = entitys;
}
}
Then, I call it from my DataAcces Layer, like this:
public List <Entities.ENFactura> ListaxIdFactura (SqlTransaction Tr, Entities.ENFactura oBEFactura)
{
SqlConnection Cn = new SqlConnection();
Cn = _Connection.ConexionSEG();
List<Entities.ENFactura> loBEFactura = new List<Entities.ENFactura>();
using (Cn)
{
Cn.Open();
SqlDataReader drd = (odaSQL.fSelDrd(Cn, Tr, "Pa_CC_Factura_Listar_x_IdProveedor", oBEFactura));
if (drd != null)
{
if (drd.HasRows)
{
mapeador.MapearDataReaderListaObjetos <ENFactura>(drd, loBEFactura);
}
}
}
return (loBEFactura);
}
So, this way, the DAL gets a datareader, map it to a list of business entities, and return it to the Business Logic Layer.
This class (MapeadorDataReaderListaObjetos) has some issues still, particularly at:
info.SetValue(newObject, _valor, null);
newObject and _valor must be the same type or you'll get an exception (conversion from System.Int64 to System.Int32, in case your entity property is Int32 and its corresponding field at the database table is bigint, for example).
Also, if an entity property is another entity, this will not work, because datareaders do not return entity objects.
Obviously, this can be improved.
Regarding reflection and delegates, i found this article: Reflection - Slow or Fast? Demonstration with Solutions, by Abhishek Sur, at
http://www.abhisheksur.com/2010/11/reflection-slow-or-faster-demonstration.html
Another good article is: Dodge Common Performance Pitfalls to Craft Speedy Applications, by Joel Pobar, at http://msdn.microsoft.com/en-us/magazine/cc163759.aspx.
Hope this helps.
I think the best way to do it is to get the getter or setter method, convert it to a delegate, and work with the delegate, there is no faster way:
PropertyInfo propertyInfoProperty1 = type.GetType().GetProperty("Property1");
Func<TYPE, string> get_Property1 = (Func<TYPE, string>)Delegate.CreateDelegate(typeof(Func<TYPE, string>), propertyInfoProperty1.GetGetMethod());
Then call the getter method:
string value = get_Property1(type);
You can cache the delegates.
Dynamic assembly should help with the concern about reflection performance. Somebody has implemented property assessors using dynamic assembly here.