I am versed in the basics of Expression Trees, but I am having difficulty with this implementation:
I need to modify an instance of a class/interface, by finding every string property and encrypting it. I also need to recursively do the same to every "child" class/interface property, all the way down the rabbit hole. This includes IEnumerable<T> where T : class as well. I have the encryption sorted, but creating the expression try to do this for any T passed in is frankly beyond my understanding at this point.
I tried implementing this with reflection but performance quickly became an issue - here's what I've got so far:
Using the AggregateHelper and PropertyHelper classes from this post I was able to perform the basic string encryption:
Simple Test Classes:
public class SimpleEncryptionTest
{
public string String1 { get; set; }
}
public class NestedClassEncryptionTest
{
public string SomeString { get; set; }
public SimpleEncryptionTest Inner { get; set; }
}
public class ListClassEncryptionTest
{
public List<string> StringList { get; set; }
}
The unit tests I'm using to verify results:
[TestMethod]
public void Encryption_works_on_a_simple_class()
{
var testString = "this is only a test. If this had been a real emergency...";
var sut = new SimpleEncryptionTest() { String1 = testString };
sut.EncryptStringProperties();
Assert.AreNotEqual(testString, sut.String1);
}
[TestMethod]
public void Round_trip_works_on_a_simple_class()
{
var testString = "what string should I use?";
var sut = new SimpleEncryptionTest() { String1 = testString };
sut.EncryptStringProperties();
sut.DecryptStringProperties();
Assert.AreEqual(testString, sut.String1);
}
[TestMethod]
public void Round_trip_works_in_nested_class_scenario()
{
var outerString = "what is your name?";
var innerString = "Tony; what's your name?";
var sut = new NestedClassEncryptionTest{
SomeString = outerString,
Inner = new SimpleEncryptionTest(){String1 = innerString }
};
sut.EncryptStringProperties();
Assert.AreNotEqual(outerString, sut.SomeString);
Assert.AreNotEqual(innerString, sut.Inner.String1);
sut.DecryptStringProperties();
Assert.AreEqual(outerString, sut.SomeString);
Assert.AreEqual(innerString, sut.Inner.String1);
}
[TestMethod]
public void Round_trip_works_on_lists()
{
var testone = "one";
var testtwo = "two";
var testStrings = new List<string>() { testone, testtwo };
var sut = new ListClassEncryptionTest() { StringList = testStrings };
sut.EncryptStringProperties();
Assert.AreNotEqual(testone, sut.StringList[0]);
Assert.AreNotEqual(testtwo, sut.StringList[1]);
sut.DecryptStringProperties();
Assert.AreEqual(testone, sut.StringList[0]);
Assert.AreEqual(testtwo, sut.StringList[1]);
}
And the extension methods I'm using to Encrypt/Decrypt the values:
/// <summary>
/// Iterates through an Object's properties and encrypts any strings it finds,
/// recursively iterating any class or interface Properties.
/// </summary>
/// <typeparam name="T">Any type</typeparam>
/// <param name="genericObj">The object to encrypt</param>
/// <returns>The original object, with its string values encrypted.</returns>
public static T EncryptStringProperties<T>(this T obj)
where T : class
{
return Crypt(obj, EncryptStringAction);
}
/// <summary>
/// Iterates through an Object's properties and decrypts any strings it finds,
/// recursively iterating any class or interface Properties.
/// </summary>
/// <typeparam name="T">Any type</typeparam>
/// <param name="genericObj">The object to decrypt</param>
/// <returns>The original object, with its string values decrypted.</returns>
public static T DecryptStringProperties<T>(this T obj)
{
return Crypt(obj, DecryptStringAction);
}
private static T Crypt<T>(T obj, Action<PropertyHelper<T, string>, T> action)
{
var stringProperties = new AggregateHelper<T, string>();
foreach (var property in stringProperties.Properties)
{
var propertyHelper = stringProperties[property];
action(propertyHelper, obj);
}
// how do I find the nested classes, interfaces and lists
// using Expression Trees to feed them through the same processing?
return obj;
}
private static void EncryptStringAction<T>(PropertyHelper<T, string> prop, T genericObj)
{
var plainTextValue = (string)prop.GetValue(genericObj);
prop.SetValue(genericObj, plainTextValue.ToEncryptedString());
}
private static void DecryptStringAction<T>(PropertyHelper<T, string> prop, T genericObj)
{
var encryptedValue = (string)prop.GetValue(genericObj);
prop.SetValue(genericObj, encryptedValue.ToDecryptedString());
}
This works great as far as it goes; it encrypts string properties on any object, but I need to take it further - I need some way to recursively go "down the rabbit hole" - to create an AggregateHelper that selects properties that are instance objects (classes or interfaces) and feed those through the .EncryptStringProperties() extension method, as well as handling any IEnumerable with string values.
Try the following extension. It generates replacement Expression Tree for each object and compiles delegates for fast string replacement. All delegates are cached and replacement should be fast. It also handles recursive references and should omit Stack Overflow.
StringPropReplacer.ReplaceStrings(obj, s => Encrypt(s));
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
namespace StringReplacer
{
public static class StringPropReplacer
{
private static ParameterExpression _strParam = Expression.Parameter(typeof(string), "str");
private static ParameterExpression _originalValue = Expression.Variable(typeof(string), "original");
private static ParameterExpression _newValue = Expression.Variable(typeof(string), "newValue");
private static ParameterExpression _visitedParam = Expression.Parameter(typeof(HashSet<object>), "visited");
private static ParameterExpression _replacerParam = Expression.Parameter(typeof(Func<string, string>), "replacer");
private static ParameterExpression[] _blockVariables = new [] {_originalValue, _newValue};
private static void ReplaceObject<T>(T obj, HashSet<object> visited, Func<string, string> replacer)
{
ReflectionHolder<T>.ReplaceObject(obj, visited, replacer);
}
private static void ReplaceObjects<T>(IEnumerable<T> objects, HashSet<object> visited, Func<string, string> replacer)
{
ReflectionHolder<T>.ReplaceObjects(objects, visited, replacer);
}
public class ObjectReferenceEqualityComparer<T> : IEqualityComparer<T>
where T : notnull
{
public static IEqualityComparer<T> Default = new ObjectReferenceEqualityComparer<T>();
#region IEqualityComparer<T> Members
public bool Equals(T x, T y)
{
return ReferenceEquals(x, y);
}
public int GetHashCode(T obj)
{
if (obj == null)
return 0;
return RuntimeHelpers.GetHashCode(obj);
}
#endregion
}
private static class ReflectionHolder<T>
{
private static Action<T, HashSet<object>, Func<string, string>> _replacer;
static ReflectionHolder()
{
var objParam = Expression.Parameter(typeof(T), "obj");
var blockBody = new List<Expression>();
foreach (var prop in typeof(T).GetProperties())
{
if (prop.PropertyType == typeof(string) && prop.CanRead && prop.CanWrite)
{
var propExpression = Expression.MakeMemberAccess(objParam, prop);
blockBody.Add(Expression.Assign(_originalValue, propExpression));
blockBody.Add(Expression.Assign(_newValue, Expression.Invoke(_replacerParam, _originalValue)));
blockBody.Add(Expression.IfThen(Expression.NotEqual(_originalValue, _newValue),
Expression.Assign(propExpression, _newValue)));
}
else if (typeof(IEnumerable).IsAssignableFrom(prop.PropertyType))
{
var intf = prop.PropertyType
.GetInterfaces()
.FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>));
if (intf != null)
{
var propExpression = Expression.MakeMemberAccess(objParam, prop);
blockBody.Add(Expression.Call(typeof(StringPropReplacer), "ReplaceObjects",
intf.GetGenericArguments(), propExpression, _visitedParam, _replacerParam
));
}
}
else if (prop.PropertyType.IsClass)
{
var propExpression = Expression.MakeMemberAccess(objParam, prop);
blockBody.Add(Expression.Call(typeof(StringPropReplacer), "ReplaceObject",
new[] {prop.PropertyType}, propExpression, _visitedParam, _replacerParam
));
}
}
if (blockBody.Count == 0)
_replacer = (o, v, f) => { };
else
{
var replacerExpr = Expression.Lambda<Action<T, HashSet<object>, Func<string, string>>>(
Expression.Block(_blockVariables, blockBody), objParam, _visitedParam, _replacerParam);
_replacer = replacerExpr.Compile();
}
}
public static void ReplaceObject(T obj, HashSet<object> visited, Func<string, string> replacer)
{
if (obj == null)
return;
if (!visited.Add(obj))
return;
_replacer(obj, visited, replacer);
}
public static void ReplaceObjects(IEnumerable<T> objects, HashSet<object> visited, Func<string, string> replacer)
{
if (objects == null)
return;
if (!visited.Add(objects))
return;
foreach (var obj in objects)
{
ReplaceObject(obj, visited, replacer);
}
}
}
public static void ReplaceStrings<T>(T obj, Func<string, string> replacer)
{
ReplaceObject(obj, new HashSet<object>(ObjectReferenceEqualityComparer<object>.Default), replacer);
}
}
}
Related
I'm trying to make a utility function that can Swap two property values given by two lambda expressions - assuming that both expressions indicate properties that have a getter and a setter:
Swap(() => John.Lunch, () => Jimmy.Lunch);
I imagine the method would need to look something like this, but I'm having trouble pulling it together.
private static void Swap<TProperty>(
Expression<Func<TProperty>> first,
Expression<Func<TProperty>> second)
{
PropertyInfo firstProp = (PropertyInfo)((MemberExpression)first.Body).Member;
PropertyInfo secondProp = (PropertyInfo)((MemberExpression)second.Body).Member;
object firstObj = (((first.Body as MemberExpression).Expression as MemberExpression)
.Expression as ConstantExpression).Value;
object secondObj = (((second.Body as MemberExpression).Expression as MemberExpression)
.Expression as ConstantExpression).Value;
TProperty temp = (TProperty)firstProp.GetValue(firstObj);
firstProp.SetValue(firstObj, secondProp.GetValue(secondObj));
secondProp.SetValue(secondObj, temp);
}
Getting to the "subject" object of the expression is proving to be difficult, although I'm pretty sure it's possible.
You can write the swap itself with Expression trees:
private static void Swap<TProperty>(
Expression<Func<TProperty>> first,
Expression<Func<TProperty>> second)
{
var firstMember = first.Body as MemberExpression;
var secondMember = second.Body as MemberExpression;
var variable = Expression.Variable(typeof(TProperty));
var firstMemberAccess = Expression.MakeMemberAccess(firstMember.Expression, firstMember.Member);
var secondMemberAccess = Expression.MakeMemberAccess(secondMember.Expression, secondMember.Member);
var block = Expression.Block(new []{ variable },
Expression.Assign(variable, firstMemberAccess),
Expression.Assign(firstMemberAccess, secondMemberAccess),
Expression.Assign(secondMemberAccess, variable)
);
Expression.Lambda<Action>(block).Compile()();
}
Example:
class A { public int P { get; set; } }
class B { public int P2 { get; set; } }
var a = new A { P = 5 };
var b = new B { P2 = 10 };
Swap(() => a.P, () => b.P2);
I tried to write code as self explanatory as possible so I will let the comments do the talking. The code needs error checking but I will leave it up to you. Here is the working minimum. I chose a static class for Swapper but you can choose to use as instance object then use DI container too.
public static class Swapper
{
/// <summary>
/// Key used for look-ups.
/// </summary>
private struct Key
{
private readonly Type _tt1;
private readonly Type _tt2;
private readonly MemberInfo _m11;
private readonly MemberInfo _m12;
public Key(Type t1, Type t2, MemberInfo m1, MemberInfo m2)
{
_tt1 = t1;
_tt2 = t2;
_m11 = m1;
_m12 = m2;
}
public override bool Equals(object obj)
{
switch (obj)
{
case Key key:
return _tt1 == key._tt1 && _tt2 == key._tt2 && _m11 == key._m11 && _m12 == key._m12;
default:
return false;
}
}
public override int GetHashCode()
{
unchecked
{
var hashCode = (_tt1 != null ? _tt1.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (_tt2 != null ? _tt2.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (_m11 != null ? _m11.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (_m12 != null ? _m12.GetHashCode() : 0);
return hashCode;
}
}
}
/// <summary>
/// This is the cache used for compiled actions. Because compiling is time consuming, I think it is better to cache.
/// </summary>
private static readonly ConcurrentDictionary<Key, object> CompiledActionsCache = new ConcurrentDictionary<Key, object>();
/// <summary>
/// Main Function which does the swapping
/// </summary>
public static void Swap<TSource, TDestination, TPropertySource>(TSource source, TDestination destination, Expression<Func<TSource, TPropertySource>> first, Expression<Func<TDestination, TPropertySource>> second)
{
//Try to get value from the cache or if it is cache miss then use Factory method to create Compiled Action
var swapper = (Action<TSource, TDestination>)CompiledActionsCache.GetOrAdd(GetKey(first, second), k => Factory(first, second));
//Do the actual swapping.
swapper(source, destination);
}
/// <summary>
/// Our factory method which does all the heavy lfiting fo creating swapping actions.
/// </summary>
private static Action<TSource, TDestination> Factory<TSource, TDestination, TPropertySource>(Expression<Func<TSource, TPropertySource>> first, Expression<Func<TDestination, TPropertySource>> second)
{
//////////////This is our aim/////////////
//// var temp = obj1.Property; /////
//// obj1.Property = obj2.Property; /////
//// obj2.Property = temp; /////
//////////////////////////////////////////
// Temp value for required for swapping
var tempValue = Expression.Variable(typeof(TPropertySource), "temp_value");
// Expression assignment
// first.body is already accesing property, just use it as it is :)
var assignToTemp = Expression.Assign(tempValue, first.Body);
// Expression assignment
// second.body is already accesing property, just use it as it is :)
var secondToFirst = Expression.Assign(first.Body, second.Body);
// Final switch here
var tempToSecond = Expression.Assign(second.Body, tempValue);
// Define an expression block which has all the above assignments as expressions
var blockExpression = Expression.Block(new[] { tempValue }, assignToTemp, secondToFirst, tempToSecond);
// An expression itself is not going to swap values unless it is compiled into a delegate
// (obj1, obj2) => blockExpression from the previous line
return Expression.Lambda<Action<TSource, TDestination>>(blockExpression, first.Parameters[0], second.Parameters[0]).Compile();
}
/// <summary>
/// Key creator method.
/// </summary>
private static Key GetKey<TSource, TDestination, TPropertySource>(Expression<Func<TSource, TPropertySource>> first, Expression<Func<TDestination, TPropertySource>> second)
{
var sourceType = typeof(TSource);
var destinationType = typeof(TDestination);
var sourcePropertyInfo = ((MemberExpression)first.Body).Member;
var destinationPorpertyInfo = ((MemberExpression)second.Body).Member;
return new Key(sourceType, destinationType, sourcePropertyInfo, destinationPorpertyInfo);
}
}
Let us see how to use Swapper in action
public class From
{
public byte FromProperty { get; set; }
}
public class To
{
public byte ToProperty { get; set; }
}
var from = new From();
from.FromProperty = 5;
var to = new To();
Swapper.Swap(from, to, f => f.FromProperty, t => t.ToProperty);
In my project I need to transform data between several classes so I created a class DataMapper that is used for strong-typed mapping of properties from two different classes. When properties in the pair need to be modified I store two delegates (converters) for this purpose.
Then the DataMapper has two methods Update(T source, S target) and Update(S source, T target) that use these mappings to provide the tranformation.
public class DataMapper<TSourceType, TTargetType> : IDataUpdater<TSourceType, TTargetType> {
private readonly IDictionary<PropertyInfo, PropertyInfo> _sourceToTargetMap = new Dictionary<PropertyInfo, PropertyInfo>();
private readonly IDictionary<PropertyInfo, object> _converters = new Dictionary<PropertyInfo, object>();
public DataMapper<TSourceType, TTargetType> Map<TSourceValue, TTargetValue>(
Expression<Func<TSourceType, TSourceValue>> sourcePropExpr,
Expression<Func<TTargetType, TTargetValue>> targetPropExpr)
{
_sourceToTargetMap.Add(sourcePropExpr.AsPropertyInfo(), targetPropExpr.AsPropertyInfo());
return this;
}
public DataMapper<TSourceType, TTargetType> Map<TSourceValue, TTargetValue>(
Expression<Func<TSourceType, TSourceValue>> sourcePropExpr,
Expression<Func<TTargetType, TTargetValue>> targetPropExpr,
Func<TSourceValue, TTargetValue> sourceToTargetConverter,
Func<TTargetValue, TSourceValue> targetToSourceConverter)
{
_sourceToTargetMap.Add(sourcePropExpr.AsPropertyInfo(), targetPropExpr.AsPropertyInfo());
_converters.Add(sourcePropExpr.AsPropertyInfo(), sourceToTargetConverter);
_converters.Add(targetPropExpr.AsPropertyInfo(), targetToSourceConverter);
return this;
}
public void Update(TSourceType source, TTargetType target) {
foreach (var keyValuePair in _sourceToTargetMap) {
var sourceProp = keyValuePair.Key;
var targetProp = keyValuePair.Value;
Update(source, target, sourceProp, targetProp);
}
}
public void Update(TTargetType source, TSourceType target) {
foreach (var keyValuePair in _sourceToTargetMap) {
var sourceProp = keyValuePair.Value;
var targetProp = keyValuePair.Key;
Update(source, target, sourceProp, targetProp);
}
}
private void Update(
object source,
object target,
PropertyInfo sourceProperty,
PropertyInfo targetProperty)
{
var sourceValue = sourceProperty.GetValue(source);
if (_converters.ContainsKey(sourceProperty)) {
sourceValue = typeof(InvokeHelper<,>)
.MakeGenericType(sourceProperty.PropertyType, targetProperty.PropertyType)
.InvokeMember("Call", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, new[] { _converters[sourceProperty], sourceValue });
}
targetProperty.SetValue(target, sourceValue);
}
}
Here is the usage:
public SomeClass {
private static readonly IDataUpdater<SomeClass, SomeOtherClass> _dataMapper = new DataMapper<SomeClass, SomeOtherClass>()
.Map(x => x.PropertyA, y => y.PropertyAA)
.Map(x => x.PropertyB, y => y.PropertyBB, x => Helper.Encrypt(x), y => Helper.Decrypt(y));
public string PropertyA { get; set; }
public string PropertyB { get; set; }
public void LoadFrom(SomeOtherClass source) {
_dataMapper.Update(source, this);
}
public void SaveTo(SomeOtherClass target) {
_dataMapper.Update(this, target);
}
}
You can see in class DataHelper in the last overload of method Update that when I want to call the stored converter function, I use helper class InvokeHelper, because I didn't found other way how to call boxed delegate Func. Code for class InvokeHelper is simple - just single static method:
public static class InvokeHelper<TSource, TTarget> {
public static TTarget Call(Func<TSource, TTarget> converter, TSource source) {
return converter(source);
}
}
Is there a way how to do it without reflection? I need to optimalize these transformations for speed.
Thanks.
You can use Delegate.DynamicInvoke to invoke the delegate. Or, use dynamic:
((dynamic)(Delegate)_converters[sourceProperty])(sourceValue);
The (Delegate) cast is not necessary. It's for documentation and runtime assertion purposes. Leave it out if you don't like it.
Actually, you better use delegate instead of object in the dictionary.
If it were me, I would use a little meta-coding with expressions to create a list of compiled and strongly typed delegates. When you call the Update method, you can go through each Action in the list and update the destination from the source.
No reflection and all of the compiling and such is done once, ahead of the Update call.
public class DataMapper<TSourceType, TTargetType> : IDataUpdater<TSourceType, TTargetType>
{
List<Action<TSourceType, TTargetType>> _mappers = new List<Action<TSourceType, TTargetType>>();
DataMapper<TTargetType, TSourceType> _reverseMapper;
public DataMapper() : this(false) { }
public DataMapper(bool isReverse)
{
if (!isReverse)
{
_reverseMapper = new DataMapper<TTargetType, TSourceType>(isReverse: true);
}
}
public DataMapper<TSourceType, TTargetType> Map<TSourceValue, TTargetValue>(
Expression<Func<TSourceType, TSourceValue>> sourcePropExpr,
Expression<Func<TTargetType, TTargetValue>> targetPropExpr)
{
var mapExpression = Expression.Assign(targetPropExpr.Body, sourcePropExpr.Body);
_mappers.Add(
Expression.Lambda<Action<TSourceType, TTargetType>>(
mapExpression,
sourcePropExpr.Parameters[0],
targetPropExpr.Parameters[0])
.Compile());
if (_reverseMapper != null) _reverseMapper.Map(targetPropExpr, sourcePropExpr);
return this;
}
public DataMapper<TSourceType, TTargetType> Map<TSourceValue, TTargetValue>(
Expression<Func<TSourceType, TSourceValue>> sourcePropExpr,
Expression<Func<TTargetType, TTargetValue>> targetPropExpr,
Func<TSourceValue, TTargetValue> sourceToTargetConverter,
Func<TTargetValue, TSourceValue> targetToSourceConverter)
{
var convertedSourceExpression = Expression.Invoke(Expression.Constant(sourceToTargetConverter), sourcePropExpr.Body);
var mapExpression = Expression.Assign(targetPropExpr.Body, convertedSourceExpression);
_mappers.Add(
Expression.Lambda<Action<TSourceType, TTargetType>>(
mapExpression,
sourcePropExpr.Parameters[0],
targetPropExpr.Parameters[0])
.Compile());
if (_reverseMapper != null) _reverseMapper.Map(targetPropExpr, sourcePropExpr, targetToSourceConverter, sourceToTargetConverter);
return this;
}
public void Update(TSourceType source, TTargetType target)
{
foreach (var mapper in _mappers)
{
mapper(source, target);
}
}
public void Update(TTargetType source, TSourceType target)
{
if (_reverseMapper != null)
{
_reverseMapper.Update(source, target);
}
else
{
throw new Exception("Reverse mapper is null. Did you reverse twice?");
};
}
}
The expression is built by taking the expressions that are passed in and using them as parts for the new expression.
Say you called .Map(x => x.PropertyA, y => y.PropertyAA). You now have 2 expressions each with a parameter x and y and each with a body x.PropertyA and y.PropertyAA.
Now you want to re-assemble these expression parts into an assignment expression like y.PropertyAA = x.PropertyA. This is done in the line var mapExpression = Expression.Assign(targetPropExpr.Body, sourcePropExpr.Body); which gives you an expected expression.
Now when you call Expression.Lambda, you are incorporating the parameters (x,y) into a new expression that looks like (x,y) = > y.PropertyAA = x.PropertyA.
Before you can execute this, you need to compile it, hence the .Compile(). But since you only need to compile this once for any given map, you can compile and store the result. The uncompiled expression is of type Expression<Action<TSourceType,TTargetType>> and after it is compiled the resulting type is Action<TSourceType,TTargetType>
Is this the fastest way to update a property using reflection? Assume the property is always an int:
PropertyInfo counterPropertyInfo = GetProperty();
int value = (int)counterPropertyInfo.GetValue(this, null);
counterPropertyInfo.SetValue(this, value + 1, null);
I did some benchmarking here when you know the type arguments (a non generic approach wont be very different). CreateDelegate would be the fastest approach for a property if you can't directly access it. With CreateDelegate you get a direct handle to GetGetMethod and GetSetMethod of the PropertyInfo, hence reflection is not used every time.
public static Func<S, T> BuildGetAccessor<S, T>(Expression<Func<S, T>> propertySelector)
{
return propertySelector.GetPropertyInfo().GetGetMethod().CreateDelegate<Func<S, T>>();
}
public static Action<S, T> BuildSetAccessor<S, T>(Expression<Func<S, T>> propertySelector)
{
return propertySelector.GetPropertyInfo().GetSetMethod().CreateDelegate<Action<S, T>>();
}
// a generic extension for CreateDelegate
public static T CreateDelegate<T>(this MethodInfo method) where T : class
{
return Delegate.CreateDelegate(typeof(T), method) as T;
}
public static PropertyInfo GetPropertyInfo<S, T>(this Expression<Func<S, T>> propertySelector)
{
var body = propertySelector.Body as MemberExpression;
if (body == null)
throw new MissingMemberException("something went wrong");
return body.Member as PropertyInfo;
}
So now you call:
TestClass cwp = new TestClass();
var access = BuildGetAccessor((TestClass t) => t.AnyValue);
var result = access(cwp);
Or even better you can encapsulate the logic in a dedicated class to have a get and set methods on it.
Something like:
public class Accessor<S>
{
public static Accessor<S, T> Create<T>(Expression<Func<S, T>> memberSelector)
{
return new GetterSetter<T>(memberSelector);
}
public Accessor<S, T> Get<T>(Expression<Func<S, T>> memberSelector)
{
return Create(memberSelector);
}
public Accessor()
{
}
class GetterSetter<T> : Accessor<S, T>
{
public GetterSetter(Expression<Func<S, T>> memberSelector) : base(memberSelector)
{
}
}
}
public class Accessor<S, T> : Accessor<S>
{
Func<S, T> Getter;
Action<S, T> Setter;
public bool IsReadable { get; private set; }
public bool IsWritable { get; private set; }
public T this[S instance]
{
get
{
if (!IsReadable)
throw new ArgumentException("Property get method not found.");
return Getter(instance);
}
set
{
if (!IsWritable)
throw new ArgumentException("Property set method not found.");
Setter(instance, value);
}
}
protected Accessor(Expression<Func<S, T>> memberSelector) //access not given to outside world
{
var prop = memberSelector.GetPropertyInfo();
IsReadable = prop.CanRead;
IsWritable = prop.CanWrite;
AssignDelegate(IsReadable, ref Getter, prop.GetGetMethod());
AssignDelegate(IsWritable, ref Setter, prop.GetSetMethod());
}
void AssignDelegate<K>(bool assignable, ref K assignee, MethodInfo assignor) where K : class
{
if (assignable)
assignee = assignor.CreateDelegate<K>();
}
}
Short and simple. You can carry around an instance of this class for every "class-property" pair you wish to get/set.
Usage:
Person p = new Person { Age = 23 };
var ageAccessor = Accessor<Person>(x => x.Age);
int age = ageAccessor[p]; //gets 23
ageAccessor[p] = 45; //sets 45
Bit bad use of indexers here, you may replace it with dedicated "Get" and "Set" methods, but very intuitive to me :)
To avoid having to specify type each time like,
var ageAccessor = Accessor<Person>(x => x.Age);
var nameAccessor = Accessor<Person>(x => x.Name);
var placeAccessor = Accessor<Person>(x => x.Place);
I made the base Accessor<> class instantiable, which means you can do
var personAccessor = new Accessor<Person>();
var ageAccessor = personAccessor.Get(x => x.Age);
var nameAccessor = personAccessor.Get(x => x.Name);
var placeAccessor = personAccessor.Get(x => x.Place);
Having a base Accessor<> class means you can treat them as one type, for eg,
var personAccessor = new Accessor<Person>();
var personAccessorArray = new Accessor<Person>[]
{
personAccessor.Get(x => x.Age),
personAccessor.Get(x => x.Name),
personAccessor.Get(x => x.Place);
};
You should look at FastMember (nuget, source code], it's really fast comparing to reflection.
I've tested these 3 implementations:
PropertyInfo.SetValue
PropertyInfo.SetMethod
FastMember
The benchmark needs a benchmark function:
static long Benchmark(Action action, int iterationCount, bool print = true)
{
GC.Collect();
var sw = new Stopwatch();
action(); // Execute once before
sw.Start();
for (var i = 0; i <= iterationCount; i++)
{
action();
}
sw.Stop();
if (print) System.Console.WriteLine("Elapsed: {0}ms", sw.ElapsedMilliseconds);
return sw.ElapsedMilliseconds;
}
A fake class:
public class ClassA
{
public string PropertyA { get; set; }
}
Some test methods:
private static void Set(string propertyName, string value)
{
var obj = new ClassA();
obj.PropertyA = value;
}
private static void FastMember(string propertyName, string value)
{
var obj = new ClassA();
var type = obj.GetType();
var accessors = TypeAccessor.Create(type);
accessors[obj, "PropertyA"] = "PropertyValue";
}
private static void SetValue(string propertyName, string value)
{
var obj = new ClassA();
var propertyInfo = obj.GetType().GetProperty(propertyName);
propertyInfo.SetValue(obj, value);
}
private static void SetMethodInvoke(string propertyName, string value)
{
var obj = new ClassA();
var propertyInfo = obj.GetType().GetProperty(propertyName);
propertyInfo.SetMethod.Invoke(obj, new object[] { value });
}
The script itself:
var iterationCount = 100000;
var propertyName = "PropertyA";
var value = "PropertyValue";
Benchmark(() => Set(propertyName, value), iterationCount);
Benchmark(() => FastMember(propertyName, value), iterationCount);
Benchmark(() => SetValue(propertyName, value), iterationCount);
Benchmark(() => SetMethodInvoke(propertyName, value), iterationCount);
Results for 100 000 iterations:
Default setter : 3ms
FastMember: 36ms
PropertyInfo.SetValue: 109ms
PropertyInfo.SetMethod: 91ms
Now you can choose yours !!!
Just be sure that you are caching the PropertyInfo somehow, so that you aren't repeatably calling type.GetProperty. Other than that it would probably be faster if you created a delegate to a method on the type that performed the increment, or like Teoman suggested make the type implement an interface and use that.
How do you give a C# Auto-Property a default value, using a custom attribute?
This is the code I want to see:
class Person
{
[MyDefault("William")]
public string Name { get; set; }
}
I am aware that there is no built in method to initialize the default using an attribute - can I write my own custom class that uses my custom attributes to initialize the default?
If you want to do it with PostSharp (as your tags suggest) then use a Lazy Loading aspect. You can see the one I built here http://programmersunlimited.wordpress.com/2011/03/23/postsharp-weaving-community-vs-professional-reasons-to-get-a-professional-license/
With an aspect you can apply default value to a single property or apply it to multiple properties with a single declaration at the class level.
Lazy loading aspect will use LocationInterceptionAspect base class.
[Serializable]
[LazyLoadingAspect(AttributeExclude=true)]
[MulticastAttributeUsage(MulticastTargets.Property)]
public class LazyLoadingAspectAttribute : LocationInterceptionAspect
{
public object DefaultValue {get; set;}
public override void OnGetValue(LocationInterceptionArgs args)
{
args.ProceedGetValue();
if (args.Value != null)
{
return;
}
args.Value = DefaultValue;
args.ProceedSetValue();
}
}
then apply the aspect like so
[LazyLoadingAspect(DefaultValue="SomeValue")]
public string MyProp { get; set; }
You could use a helper class like that:
public class DefaultValueHelper
{
public static void InitializeDefaultValues<T>(T obj)
{
var properties =
(from prop in obj.GetType().GetProperties()
let attr = GetDefaultValueAttribute(prop)
where attr != null
select new
{
Property = prop,
DefaultValue = attr.Value
}).ToArray();
foreach (var p in properties)
{
p.Property.SetValue(obj, p.DefaultValue, null);
}
}
private static DefaultValueAttribute GetDefaultValueAttribute(PropertyInfo prop)
{
return prop.GetCustomAttributes(typeof(DefaultValueAttribute), true)
.Cast<DefaultValueAttribute>()
.FirstOrDefault();
}
}
And call InitializeDefaultValues in the constructor of your class.
class Foo
{
public Foo()
{
DefaultValueHelper.InitializeDefaultValues(this);
}
[DefaultValue("(no name)")]
public string Name { get; set; }
}
EDIT: updated version, which generates and caches a delegate to do the initialization. This is to avoid using reflection every time the method is called for a given type.
public static class DefaultValueHelper
{
private static readonly Dictionary<Type, Action<object>> _initializerCache;
static DefaultValueHelper()
{
_initializerCache = new Dictionary<Type, Action<object>>();
}
public static void InitializeDefaultValues(object obj)
{
if (obj == null)
return;
var type = obj.GetType();
Action<object> initializer;
if (!_initializerCache.TryGetValue(type, out initializer))
{
initializer = MakeInitializer(type);
_initializerCache[type] = initializer;
}
initializer(obj);
}
private static Action<object> MakeInitializer(Type type)
{
var arg = Expression.Parameter(typeof(object), "arg");
var variable = Expression.Variable(type, "x");
var cast = Expression.Assign(variable, Expression.Convert(arg, type));
var assignments =
from prop in type.GetProperties()
let attr = GetDefaultValueAttribute(prop)
where attr != null
select Expression.Assign(Expression.Property(variable, prop), Expression.Constant(attr.Value));
var body = Expression.Block(
new ParameterExpression[] { variable },
new Expression[] { cast }.Concat(assignments));
var expr = Expression.Lambda<Action<object>>(body, arg);
return expr.Compile();
}
private static DefaultValueAttribute GetDefaultValueAttribute(PropertyInfo prop)
{
return prop.GetCustomAttributes(typeof(DefaultValueAttribute), true)
.Cast<DefaultValueAttribute>()
.FirstOrDefault();
}
}
If to speculate with Expressions you could make initializing delegates and cache them. It will make code much faster comparing with just pure reflection.
internal static class Initializer
{
private class InitCacheEntry
{
private Action<object, object>[] _setters;
private object[] _values;
public InitCacheEntry(IEnumerable<Action<object, object>> setters, IEnumerable<object> values)
{
_setters = setters.ToArray();
_values = values.ToArray();
if (_setters.Length != _values.Length)
throw new ArgumentException();
}
public void Init(object obj)
{
for (int i = 0; i < _setters.Length; i++)
{
_setters[i](obj, _values[i]);
}
}
}
private static Dictionary<Type, InitCacheEntry> _cache = new Dictionary<Type, InitCacheEntry>();
private static InitCacheEntry MakeCacheEntry(Type targetType)
{
var setters = new List<Action<object, object>>();
var values = new List<object>();
foreach (var propertyInfo in targetType.GetProperties())
{
var attr = (DefaultAttribute) propertyInfo.GetCustomAttributes(typeof (DefaultAttribute), true).FirstOrDefault();
if (attr == null) continue;
var setter = propertyInfo.GetSetMethod();
if (setter == null) continue;
// we have to create expression like (target, value) => ((TObj)target).setter((T)value)
// where T is the type of property and obj is instance being initialized
var targetParam = Expression.Parameter(typeof (object), "target");
var valueParam = Expression.Parameter(typeof (object), "value");
var expr = Expression.Lambda<Action<object, object>>(
Expression.Call(Expression.Convert(targetParam, targetType),
setter,
Expression.Convert(valueParam, propertyInfo.PropertyType)),
targetParam, valueParam);
var set = expr.Compile();
setters.Add(set);
values.Add(attr.DefaultValue);
}
return new InitCacheEntry(setters, values);
}
public static void Init(object obj)
{
Type targetType = obj.GetType();
InitCacheEntry init;
if (!_cache.TryGetValue(targetType, out init))
{
init = MakeCacheEntry(targetType);
_cache[targetType] = init;
}
init.Init(obj);
}
}
You could create a method like this:
public static void FillProperties<T>(T obj)
{
foreach (var property in typeof(T).GetProperties())
{
var attribute = property
.GetCustomAttributes(typeof(DefaultValueAttribute), true)
.Cast<DefaultValueAttribute>()
.SingleOrDefault();
if (attribute != null)
property.SetValue(obj, attribute.Value, null);
}
}
You can then either use a factory method that calls this method or call it directly from the constructor. Note that this usage of reflection is probably not a good idea if you create a lot of objects this way and performance is important.
I was looking at this post that describes a simple way to do databinding between POCO properties: Data Binding POCO Properties
One of the comments by Bevan included a simple Binder class that can be used to accomplish such data binding. It works great for what I need but I would like to implement some of the suggestions that Bevan made to improve the class, namely:
Checking that source and target are
assigned
Checking that the properties
identified by sourcePropertyName and
targetPropertyName exist
Checking for type compatibility
between the two properties
Also, given that specifying properties by string is error prone, you could use Linq expressions and extension methods instead. Then instead of writing
Binder.Bind( source, "Name", target, "Name")
you could write
source.Bind( Name => target.Name);
I'm pretty sure I can handle the first three (though feel free to include those changes) but I have no clue how to use Linq expressions and extension methods to be able to write code without using property name strings.
Any tips?
Here is the original code as found in the link:
public static class Binder
{
public static void Bind(
INotifyPropertyChanged source,
string sourcePropertyName,
INotifyPropertyChanged target,
string targetPropertyName)
{
var sourceProperty
= source.GetType().GetProperty(sourcePropertyName);
var targetProperty
= target.GetType().GetProperty(targetPropertyName);
source.PropertyChanged +=
(s, a) =>
{
var sourceValue = sourceProperty.GetValue(source, null);
var targetValue = targetProperty.GetValue(target, null);
if (!Object.Equals(sourceValue, targetValue))
{
targetProperty.SetValue(target, sourceValue, null);
}
};
target.PropertyChanged +=
(s, a) =>
{
var sourceValue = sourceProperty.GetValue(source, null);
var targetValue = targetProperty.GetValue(target, null);
if (!Object.Equals(sourceValue, targetValue))
{
sourceProperty.SetValue(source, targetValue, null);
}
};
}
}
The following will return a property name as a string from a lambda expression:
public string PropertyName<TProperty>(Expression<Func<TProperty>> property)
{
var lambda = (LambdaExpression)property;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = (UnaryExpression)lambda.Body;
memberExpression = (MemberExpression)unaryExpression.Operand;
}
else
{
memberExpression = (MemberExpression)lambda.Body;
}
return memberExpression.Member.Name;
}
Usage:
public class MyClass
{
public int World { get; set; }
}
...
var c = new MyClass();
Console.WriteLine("Hello {0}", PropertyName(() => c.World));
UPDATE
public static class Extensions
{
public static void Bind<TSourceProperty, TDestinationProperty>(this INotifyPropertyChanged source, Expression<Func<TSourceProperty, TDestinationProperty>> bindExpression)
{
var expressionDetails = GetExpressionDetails<TSourceProperty, TDestinationProperty>(bindExpression);
var sourcePropertyName = expressionDetails.Item1;
var destinationObject = expressionDetails.Item2;
var destinationPropertyName = expressionDetails.Item3;
// Do binding here
Console.WriteLine("{0} {1}", sourcePropertyName, destinationPropertyName);
}
private static Tuple<string, INotifyPropertyChanged, string> GetExpressionDetails<TSourceProperty, TDestinationProperty>(Expression<Func<TSourceProperty, TDestinationProperty>> bindExpression)
{
var lambda = (LambdaExpression)bindExpression;
ParameterExpression sourceExpression = lambda.Parameters.FirstOrDefault();
MemberExpression destinationExpression = (MemberExpression)lambda.Body;
var memberExpression = destinationExpression.Expression as MemberExpression;
var constantExpression = memberExpression.Expression as ConstantExpression;
var fieldInfo = memberExpression.Member as FieldInfo;
var destinationObject = fieldInfo.GetValue(constantExpression.Value) as INotifyPropertyChanged;
return new Tuple<string, INotifyPropertyChanged, string>(sourceExpression.Name, destinationObject, destinationExpression.Member.Name);
}
}
Usage:
public class TestSource : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string Name { get; set; }
}
public class TestDestination : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string Id { get; set; }
}
class Program
{
static void Main(string[] args)
{
var x = new TestSource();
var y = new TestDestination();
x.Bind<string, string>(Name => y.Id);
}
}
This question is very similar to: Retrieving Property name from lambda expression
(Cross-posting answer from https://stackoverflow.com/a/17220748/1037948)
I don't know if you need to bind to "subproperties", but inspecting the lambda.Body for Member.Name will only return the "final" property, not a "fully-qualified" property.
ex) o => o.Thing1.Thing2 would result in Thing2, not Thing1.Thing2.
This is problematic when trying to use this method to simplify EntityFramework DbSet.Include(string) with expression overloads.
So you can "cheat" and parse the Expression.ToString instead. Performance seemed comparable in my tests, so please correct me if this is a bad idea.
The Extension Method
/// <summary>
/// Given an expression, extract the listed property name; similar to reflection but with familiar LINQ+lambdas. Technique #via https://stackoverflow.com/a/16647343/1037948
/// </summary>
/// <remarks>Cheats and uses the tostring output -- Should consult performance differences</remarks>
/// <typeparam name="TModel">the model type to extract property names</typeparam>
/// <typeparam name="TValue">the value type of the expected property</typeparam>
/// <param name="propertySelector">expression that just selects a model property to be turned into a string</param>
/// <param name="delimiter">Expression toString delimiter to split from lambda param</param>
/// <param name="endTrim">Sometimes the Expression toString contains a method call, something like "Convert(x)", so we need to strip the closing part from the end</pa ram >
/// <returns>indicated property name</returns>
public static string GetPropertyName<TModel, TValue>(this Expression<Func<TModel, TValue>> propertySelector, char delimiter = '.', char endTrim = ')') {
var asString = propertySelector.ToString(); // gives you: "o => o.Whatever"
var firstDelim = asString.IndexOf(delimiter); // make sure there is a beginning property indicator; the "." in "o.Whatever" -- this may not be necessary?
return firstDelim < 0
? asString
: asString.Substring(firstDelim+1).TrimEnd(endTrim);
}//-- fn GetPropertyNameExtended
(Checking for the delimiter might even be overkill)
This is likely more than or not exactly what you asked for but I've done something similar to handle mapping of a property between two objects:
public interface IModelViewPropagationItem<M, V>
where M : BaseModel
where V : IView
{
void SyncToView(M model, V view);
void SyncToModel(M model, V view);
}
public class ModelViewPropagationItem<M, V, T> : IModelViewPropagationItem<M, V>
where M : BaseModel
where V : IView
{
private delegate void VoidDelegate();
public Func<M, T> ModelValueGetter { get; private set; }
public Action<M, T> ModelValueSetter { get; private set; }
public Func<V, T> ViewValueGetter { get; private set; }
public Action<V, T> ViewValueSetter { get; private set; }
public ModelViewPropagationItem(Func<M, T> modelValueGetter, Action<V, T> viewValueSetter)
: this(modelValueGetter, null, null, viewValueSetter)
{ }
public ModelViewPropagationItem(Action<M, T> modelValueSetter, Func<V, T> viewValueGetter)
: this(null, modelValueSetter, viewValueGetter, null)
{ }
public ModelViewPropagationItem(Func<M, T> modelValueGetter, Action<M, T> modelValueSetter, Func<V, T> viewValueGetter, Action<V, T> viewValueSetter)
{
this.ModelValueGetter = modelValueGetter;
this.ModelValueSetter = modelValueSetter;
this.ViewValueGetter = viewValueGetter;
this.ViewValueSetter = viewValueSetter;
}
public void SyncToView(M model, V view)
{
if (this.ViewValueSetter == null || this.ModelValueGetter == null)
throw new InvalidOperationException("Syncing to View is not supported for this instance.");
this.ViewValueSetter(view, this.ModelValueGetter(model));
}
public void SyncToModel(M model, V view)
{
if (this.ModelValueSetter == null || this.ViewValueGetter == null)
throw new InvalidOperationException("Syncing to Model is not supported for this instance.");
this.ModelValueSetter(model, this.ViewValueGetter(view));
}
}
This allows you to create an instance of this object and then use "SyncToModel" and "SyncToView" to move values back and forth. The following piece that goes with this allows you to group multiple of these things and move data back and forth with one call:
public class ModelViewPropagationGroup<M, V> : List<IModelViewPropagationItem<M, V>>
where M : BaseModel
where V : IView
{
public ModelViewPropagationGroup(params IModelViewPropagationItem<M, V>[] items)
{
this.AddRange(items);
}
public void SyncAllToView(M model, V view)
{
this.ForEach(o => o.SyncToView(model, view));
}
public void SyncAllToModel(M model, V view)
{
this.ForEach(o => o.SyncToModel(model, view));
}
}
Usage would look something like this:
private static readonly ModelViewPropagationItem<LoginModel, ILoginView, string> UsernamePI = new ModelViewPropagationItem<LoginModel, ILoginView, string>(m => m.Username.Value, (m, x) => m.Username.Value = x, v => v.Username, (v, x) => v.Username = x);
private static readonly ModelViewPropagationItem<LoginModel, ILoginView, string> PasswordPI = new ModelViewPropagationItem<LoginModel, ILoginView, string>(m => m.Password.Value, (m, x) => m.Password.Value = x, v => v.Password, (v, x) => v.Password = x);
private static readonly ModelViewPropagationGroup<LoginModel, ILoginView> GeneralPG = new ModelViewPropagationGroup<LoginModel, ILoginView>(UsernamePI, PasswordPI);
public UserPrincipal Login_Click()
{
GeneralPG.SyncAllToModel(this.Model, this.View);
return this.Model.DoLogin();
}
Hope this helps!
var pr = typeof(CCategory).GetProperties().Select(i => i.Name).ToList(); ;
declaration:
class Foo<T> {
public string Bar<T, TResult>(Expression<Func<T, TResult>> expersion)
{
var lambda = (LambdaExpression)expersion;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = (UnaryExpression)lambda.Body;
memberExpression = (MemberExpression)unaryExpression.Operand;
}
else
{
memberExpression = (MemberExpression)lambda.Body;
}
return memberExpression.Member.Name;
}
}
Usage:
var foo = new Foo<DummyType>();
var propName = foo.Bar(d=>d.DummyProperty)
Console.WriteLine(propName); //write "DummyProperty" string in shell