Here is a sample Scala code that I'm trying to rewrite in C#:
trait FuncHolder[T, TResult] {
def Value: Function1[T, TResult]
}
object FuncHolder {
def GetFunctionHolder[T, TResult](func: Function1[T, TResult]) = new FuncHolder[T, TResult] {
override def Value: Function1[T, TResult] = func
}
}
Here is where I started:
public abstract class FuncHolder<T, TResult>
{
public Func<T, TResult> Value { get; }
protected FuncHolder(Func<T, TResult> value)
{
Value = value;
}
}
public static class FuncHolder
{
public static FuncHolder<T, TResult> GetFuncHolder<T, TResult>(Func<T, TResult> func)
{
var ilBody = func.Method.GetMethodBody().GetILAsByteArray();
}
}
But then I stuck because I'm not sure if I could just copy that byte array in il.emit and it will work. So I thought maybe in 2019 there could be some ways in acheiving it without dirty magic, postsharping output binaries or something. I'm looking for a pure solution in terms of BCL and Expressions/DynamicMethods.
The most recent attempt is following:
public abstract class FuncHolder<T, TResult>
{
public abstract TResult GetResult(T value);
}
public static class FuncHolder
{
private static ModuleBuilder _builder = AssemblyBuilder
.DefineDynamicAssembly(new AssemblyName("OmsCodegen"), AssemblyBuilderAccess.Run)
.DefineDynamicModule("MainModule");
/// <summary>
/// Returns a holder that allows safely pass delegates through the network
/// </summary>
/// <param name="func">Func to hold. Note that it must be pure function, closure with external dependencies will fail</param>
public static FuncHolder<T, TResult> GetFuncHolder<T, TResult>(Func<T, TResult> func)
{
var tb = _builder.DefineType($"<{func.Method.MetadataToken}>__FuncHolder", TypeAttributes.Class | TypeAttributes.Sealed);
tb.SetParent(typeof(FuncHolder));
var baseMethod = typeof(FuncHolder<,>).GetMethod("GetResult");
Debug.Assert(baseMethod != null);
var implementation = tb.DefineMethod(baseMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual);
CopyIl(implementation, func.Method);
tb.DefineMethodOverride(implementation, baseMethod);
tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName |
MethodAttributes.RTSpecialName);
return (FuncHolder<T, TResult>) Activator.CreateInstance(tb.CreateTypeInfo().AsType());
}
private static void CopyIl(MethodBuilder implementation, MethodInfo methodInfo)
{
var ilGenerator = implementation.GetILGenerator();
var methodBody = methodInfo.GetMethodBody();
var il = methodBody?.GetILAsByteArray() ?? throw new InvalidOperationException("Cannot get method body");
foreach (var local in methodBody.LocalVariables)
ilGenerator.DeclareLocal(local.LocalType);
var opCodes = GetOpCodes(il);
for (int i = 0; i < opCodes.Length; ++i)
{
if (!opCodes[i].code.HasValue)
continue;
OpCode opCode = opCodes[i].code.Value;
if (opCode.OperandType == OperandType.InlineBrTarget)
{
ilGenerator.Emit(opCode, BitConverter.ToInt32(il, i + 1));
i += 4;
continue;
}
if (opCode.OperandType == OperandType.ShortInlineBrTarget)
{
ilGenerator.Emit(opCode, il[i + 1]);
++i;
continue;
}
if (opCode.OperandType == OperandType.InlineType)
{
Type tp = methodInfo.Module.ResolveType(BitConverter.ToInt32(il, i + 1), methodInfo.DeclaringType.GetGenericArguments(), methodInfo.GetGenericArguments());
ilGenerator.Emit(opCode, tp);
i += 4;
continue;
}
if (opCode.FlowControl == FlowControl.Call)
{
MethodInfo mi = methodInfo.Module.ResolveMethod(BitConverter.ToInt32(il, i + 1)) as MethodInfo;
if (mi == methodInfo)
ilGenerator.Emit(opCode, implementation);
else
ilGenerator.Emit(opCode, mi);
i += 4;
continue;
}
ilGenerator.Emit(opCode);
}
}
static OpCodeContainer[] GetOpCodes(byte[] data)
{
List<OpCodeContainer> opCodes = new List<OpCodeContainer>();
foreach (byte opCodeByte in data)
opCodes.Add(new OpCodeContainer(opCodeByte));
return opCodes.ToArray();
}
class OpCodeContainer
{
public OpCode? code;
byte data;
public OpCodeContainer(byte opCode)
{
data = opCode;
try
{
code = (OpCode)typeof(OpCodes).GetFields().First(t => ((OpCode)(t.GetValue(null))).Value == opCode).GetValue(null);
}
catch
{
// if it throws an exception then code should remain null
}
}
}
}
Which fails with CLR execution error.
It's not exactly what I wanted but it's the only solution I came with
public abstract class FuncHolder<T, TResult>
{
[JsonIgnore]
public abstract Func<T, TResult> Value { get; }
}
public static class FuncHolder
{
private static Dictionary<int, object> _metadataTokenToMethodDictionary = new Dictionary<int, object>();
private class FuncHolderImplementation<T, TResult> : FuncHolder<T, TResult>
{
public int MetadataToken { get; }
public FuncHolderImplementation(int metadataToken)
{
MetadataToken = metadataToken;
}
public override Func<T, TResult> Value => (Func<T, TResult>)_metadataTokenToMethodDictionary[MetadataToken];
}
public static FuncHolder<T, TResult> GetFuncHolder<T, TResult>(Func<T, TResult> func)
{
if (!_metadataTokenToMethodDictionary.ContainsKey(func.Method.MetadataToken))
{
_metadataTokenToMethodDictionary[func.Method.MetadataToken] = func;
}
return new FuncHolderImplementation<T, TResult>(func.Method.MetadataToken);
}
}
Instead of wrapping a function in the type I merely pass a metadataToken which I then convert back.
Why is this needed may be shown as following:
var holder = FuncHolder.GetFuncHolder<string, int>(s => s.Length);
var settings = new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.All};
var json = JsonConvert.SerializeObject(holder, settings);
var holder2 = JsonConvert.DeserializeObject<FuncHolder<string, int>>(json, settings);
Console.WriteLine(holder2.Value("hello"));
As you can see, this way function callback may be serialized and deserialized back.
Related
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);
}
}
}
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>
Question is simple: how to make it as simple as possible for calling code? My code is a bit stupid, but i see no way.
using System;
namespace ConsoleApplication48
{
class FunctionWithCounter<T, TResult>
{
private readonly Func<T, TResult> function;
public int Calls { get; private set; }
private FunctionWithCounter(Func<T, TResult> function)
{
Calls = 0;
this.function = function;
}
public static implicit operator FunctionWithCounter<T, TResult>(Func<T, TResult> func)
{
return new FunctionWithCounter<T, TResult>(func);
}
public TResult this[T arg]
{
get
{
Calls++;
return function(arg);
}
}
}
class Program
{
static void Main()
{
FunctionWithCounter<double, double> func = (Func<double, double>)(x => x*x);
for (int i = 0; i < 5; i++)
{
double d = func[i];
}
Console.WriteLine(func.Calls);
Console.ReadKey();
}
}
}
So i use indexer to call like this func[x] instead of func(x), and there's some difficultes (can't call like a void-method). But it's most simple i think. Any offers?
for the price of 1 extra line, I'd restore it back to at least normal function syntax. Also get rid of the implicit constructor, it doesn't really save you anything, and without it, the syntax look simpler.
class FunctionWithCounter<T, TResult>
{
public readonly Func<T, TResult> Function;
public int Calls { get; private set; }
public FunctionWithCounter(Func<T, TResult> function)
{
Calls = 0;
Function = x =>
{
Calls++;
return function(x);
};
}
}
internal class Program
{
private static void Main(string[] args)
{
var callCounter = new FunctionWithCounter<double,double>(x => x * x);
var func = callCounter.Function;
for (int i = 0; i < 5; i++)
{
double d = func(i);
}
Console.WriteLine(callCounter.Calls);
Console.ReadKey();
}
}
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.
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