Pass Property and get class - c#

This might be a trivial question but I am drawing a blank here and can't seem to find the answer online.
Basically, I am trying to create a method that takes 2 properties that are part of an INotifyPropertyChanged class as parameters (the actual properties to be used in reflection, not the property values), and keep them "in sync" like a binding.
Example
I have a class called Student with a property called int SemesterScore. I have another class called Semester with a property called int Score. Both of the classes implement IPropertyNotifyChanged.
Now, let's just assume for a moment that we can't extend any of the classes (as in my real-life scenario) and I may have multiple times in different classes I want to use this.
Basically, I want to be able to call a method in one of my classes that "links" the two properties together.. aka if one of them changes it will auto-update the other.
In non-working code, this is the basic concept:
public class Student : INotifyPropertyChanged
{
private int _semesterScore;
public int SemeseterScore
{
get { return _semesterScore; }
set { [ set property stuff with property changed] }
}
}
public class Semester: INotifyPropertyChanged
{
private int _score;
public int Score
{
get { return _score; }
set { [ set property stuff with property changed] }
}
}
public class Entry
{
public static void Main(string[] args)
{
Student student = new Student();
Semester semester = new Semester();
AttachProperties(student.SemesterScore, semester.Score); // This obviously won't work, but this is where I pass the properties in
semester.Score = 7;
Console.WriteLine(student.SemesterScore); // Output will be 7
}
public static void AttachProperties([sometype] prop1, [sometype] prop2)
{
// Sudo code
prop1.classInstance.PropertyChanged += (pe)
{
if (pe.Property == prop1.Name)
prop2.Value = prop1.Value;
}
prop2.classInstance.PropertyChanged += (pe)
{
if (pe.Property == prop2.Name)
prop1.Value = prop2.Value;
}
}
}
Is there any way to do this? I know some workarounds (aka pass the INotifyPropertyChanged classes and the property names, then do some reflection to get that to work), but the question of passing property instances around (and doing stuff with it) has come up a few times in my coding career.

One way to do this would be to use an Observable, like #itay-podhacer suggested above.
But if you want to implement using just Reflection and INotifyPropertyChanged here is how you could do it.
First, lets get SemesterScore and Student both implement INotifyPropertyChanged:
public class Student : INotifyPropertyChanged
{
private int semesterScore;
public int SemesterScore
{
get { return semesterScore; }
set
{
semesterScore = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Semester : INotifyPropertyChanged
{
private int score;
public int Score
{
get { return score; }
set
{
score = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Now lets tie the properties together, in your AttachProperties helper method. In order to do this, we will make the AttachProperties method take Expression<Func<T,object> arguments so that we avoid passing magic strings and can use Reflection to retrieve the properties names.
By the way, to run this in production you probably want to memoize that reflection code for performance.
private static void AttachProperties<T1,T2>(Expression<Func<T1, object>> property1, T1 instance1, Expression<Func<T2, object>> property2, T2 instance2)
where T1 : INotifyPropertyChanged
where T2 : INotifyPropertyChanged
{
var p1 = property1.GetPropertyInfo();
var p2 = property2.GetPropertyInfo();
//A NULL or empty PropertyName in PropertyChangeEventArgs means that all properties changed
//See: https://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.propertychanged(v=vs.110).aspx#Anchor_1
((INotifyPropertyChanged)instance1).PropertyChanged += (_, e) =>
{
if (e.PropertyName == p1.Name || string.IsNullOrEmpty(e.PropertyName))
{
SyncProperties(p1, p2, instance1, instance2);
}
};
((INotifyPropertyChanged)instance2).PropertyChanged += (_, e) =>
{
if (e.PropertyName == p2.Name || string.IsNullOrEmpty(e.PropertyName))
{
SyncProperties(p2, p1, instance2, instance1);
}
};
}
private static void SyncProperties(PropertyInfo sourceProperty, PropertyInfo targetProperty, object sourceInstance, object targetInstance)
{
var sourceValue = sourceProperty.GetValue(sourceInstance);
var targetValue = targetProperty.GetValue(targetInstance);
if (!sourceValue.Equals(targetValue))
{
targetProperty.SetValue(targetInstance, sourceValue);
}
}
And, finally, here is the Reflection code to retrieve the PropertyInfo from the arguments:
public static class ReflectionExtension
{
public static PropertyInfo GetPropertyInfo<T>(this Expression<Func<T, object>> expression)
{
var memberExpression = GetMemberExpression(expression);
return (PropertyInfo)memberExpression.Member;
}
private static MemberExpression GetMemberExpression<TModel, T>(Expression<Func<TModel, T>> expression)
{
MemberExpression memberExpression = null;
if (expression.Body.NodeType == ExpressionType.Convert)
{
var body = (UnaryExpression)expression.Body;
memberExpression = body.Operand as MemberExpression;
}
else if (expression.Body.NodeType == ExpressionType.MemberAccess)
{
memberExpression = expression.Body as MemberExpression;
}
if (memberExpression == null)
{
throw new ArgumentException("Not a member access", "expression");
}
return memberExpression;
}
}
With all this in place, you can keep two properties in sync now:
public class PropertySyncTests
{
public void Should_sync_properties()
{
var semester = new Semester();
var student = new Student();
AttachProperties(x => x.Score, semester, x => x.SemesterScore, student);
semester.Score = 7;
student.SemesterScore.ShouldBe(7);
}
}

I know some workarounds (aka pass the INotifyPropertyChanged classes and the property names, then do some reflection to get that to work), but the question of passing property instances around (and doing stuff with it) has come up a few times in my coding career.
This is, ultimately, the way to do it. However, I think one key trick that you might not be aware of is Expression Trees. It is possible to create a function that takes an Expression<Func<T>> as an argument, and then delve into the Expression Tree to discover the INotifyPropertyChanged instance and the property that's given in the argument. Usage could look like this:
AttachProperties(() => student.SemesterScore, () => semester.Score);
The arguments to AttachProperties in the example above would be Expressions with the following structure.
<LambdaExpression> () => student.SemesterScore
Body <MemberExpression> student.SemesterScore
Member <PropertyInfo> SemesterScore
Expression <MemberExpression> student
Member <FieldInfo> [closure class.]student
Expression <ConstantExpression> [closure]
Value [closure instance]
Notice that you're creating a closure by using student inside of the lambda expression, so to get the value of student you'll need to use reflection to get the value of the [closure class].student field. Getting the SemesterScore property is just a matter of casting the expressions correctly and getting the .Body.Member property from the passed-in lambda expression.

Subscribe to the PropertyChanged event of the semester:
Student student = new Student();
Semester semester = new Semester();
semester.PropertyChanged += Semester_PropertyChanged;
Then assign the new score to the student
private void Semester_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
student.SemesterScore = semester.Score;
}
This will trigger the student's PropertyChanged event and also update his SemesterScore if the score changed.

Take a look at Reactive UI Extensions, it will give you the ability to "observe" a property for changes and then update (or do whatever you want) once such change occurs.
It will allow you to do something like this:
student
.WhenAnyValue(item => item.SemeseterScore)
.Subscribe(item =>
{
semester.Score = item.SemeseterScore
});
semester
.WhenAnyValue(item => item.Score)
.Subscribe(item =>
{
item.SemeseterScore = semester.Score
});
You might need to add an Ignore flag at your class and turn it on and off inside the Subscribe code, so you don't create an endless loop of updates between the two classes.

Ok so I combined #StriplingWarrior and #Pedro's answers together to get my final result:
public static void AttachProperties<T1, T2>(Expression<Func<T1>> property1, Expression<Func<T2>> property2)
{
var instance1 = Expression.Lambda<Func<object>>(((MemberExpression)property1.Body).Expression).Compile()();
var iNotify1 = instance1 as INotifyPropertyChanged;
var prop1 = GetPropertyInfo(property1);
var instance2 = Expression.Lambda<Func<object>>(((MemberExpression)property2.Body).Expression).Compile()();
var iNotify2 = instance2 as INotifyPropertyChanged;
var prop2 = GetPropertyInfo(property2);
AttachProperty(prop1, iNotify1, prop2, iNotify2);
AttachProperty(prop2, iNotify2, prop1, iNotify1);
}
static void AttachProperty(
PropertyInfo property1,
INotifyPropertyChanged class1Instance,
PropertyInfo property2,
INotifyPropertyChanged class2Instance)
{
class2Instance.PropertyChanged += (_, propArgs) =>
{
if (propArgs.PropertyName == property2.Name || string.IsNullOrEmpty(propArgs.PropertyName))
{
var prop = property2.GetValue(class2Instance);
property1.SetValue(class1Instance, prop);
}
};
}
static PropertyInfo GetPropertyInfo<T1>(Expression<Func<T1>> property)
{
MemberExpression expression = null;
if (property.Body.NodeType == ExpressionType.Convert)
{
var body = (UnaryExpression)property.Body;
expression = body.Operand as MemberExpression;
}
else if (property.Body.NodeType == ExpressionType.MemberAccess)
{
expression = property.Body as MemberExpression;
}
if (expression == null)
{
throw new ArgumentException("Not a member access", nameof(property));
}
return expression.Member as PropertyInfo;
}
This works correctly both in the example I gave and in my real-life project. Thanks!

Related

Generic FilterClass<T> - within the class a List<predicate>(T) shall be set up, how with unknown type at runtime?

I've built a complex filter for my ICollection within my ViewModel. Now I need a similar filterfunction for a different collection and datagrid. So I guess it would suck big times if I was going to duplicate and adjust my code.
So I was going for a reusable solution.
Simple Code:
public class FilterForGrid<T>
{
public T UiModel { get; set; }
private List<Predicate<T>> criteria = new List<Predicate<T>>();
public FilterForGrid() {
// var result = typeof(T).GetProperties().ToList();
}
private bool dynamicFilter(object obj) {
T uiModel = (T)obj;
bool isIn = true;
if (criteria.Count() == 0)
return isIn;
isIn = criteria.TrueForAll(x => x(uiModel));
return isIn;
}
public void ClearFilter() {
criteria.Clear();
}
public void AddFilterArgument(string argument, string property) {
// criteria.Add(new Predicate<T>(x => x.))
}
public void FireFilter(ICollectionView toBeFilteredCollection) {
toBeFilteredCollection.Filter = dynamicFilter;
toBeFilteredCollection.Refresh();
}
}
Have a look at the method "AddFilterArgument" --> I simply want to pass the name of the property and the value over which the data shall be filtered:
public void AddFilterArgument(string argument, string property) {
criteria.Add(new Predicate<T>(x => x.property == argument))
}
But because of type inteference the property can't be found this way.
Is my attemp possible or do I have to look in another direction? If its possible please give me a clue.
Well, finally it was a much easier than expected:
Example for one of the methods
public void AddFilterPredicate(string argument, string property, OperatorsForFIlter operators) {
Predicate<T> predicate;
if (!String.IsNullOrEmpty(argument)) {
switch (operators) {
case OperatorsForFIlter.equal:
predicate = new Predicate<T>(x => x.GetType().GetProperty(property).GetValue(x, null).ToString() == argument);
break;
case OperatorsForFIlter.contains:
predicate = new Predicate<T>(x => x.GetType().GetProperty(property).GetValue(x, null).ToString().Contains(argument));
break;
default:
predicate = null;
break;
}
} else { predicate = new Predicate<T>(x => x.GetType().GetProperty(property).GetValue(x, null).ToString().Contains(argument)); }
InsertIntoCriteriaCatalogue(property, predicate);
}
This line here was exactly what I was asking for:
new Predicate<T>(x => x.GetType().GetProperty(property).GetValue(x, null).ToString().Contains(argument));
I was looking for a way to pass a name of a property as parameter and the value of the property through which the list should be filtered.
Now I can use the dynamic generic filter for all my data in every grid.

How to set value of property where there is no setter

I have seen various questions raised and answered where we can invoke a private setter using reflection such as this one:
Is it possible to get a property's private setter through reflection?
However I have some code which has a property i need to set but cant because there is no setter, I cant add a setter as this isn't my code. Is there a way to somehow set the value using reflection in this scenario?
I do not suggest doing this on your application but for testing purpose it may be usefull...
Assuming you have:
public class MyClass
{
public int MyNumber {get;}
}
You could do this if its for test purpose, I would not suggest to use this in your runtime code:
var field = typeof(MyClass).GetField("<MyNumber>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
field.SetValue(anIstanceOfMyClass, 3);
You have to keep in mind that a property is just syntactic sugar for a pair of methods. One method (the getter) returns a value of the property type and one method (the setter) accepts a value of the property type.
There is no requirement that the getter and setter actually get or set anything. They're just methods, so they're allowed to do anything. The only requirement is that the getter return a value. From the outside there's no way you can really tell if there is a backing field. The getter could be getting computed every time it's called. It may be based on other properties.
So, no, there isn't really any way in general to "set" a property that doesn't have a setter.
Adding a practical use case to #abyte0's answer.
Some libraries make use of reflection to set properties this way. For example, see this sample code from https://github.com/natemcmaster/CommandLineUtils:
using System;
using McMaster.Extensions.CommandLineUtils;
public class Program
{
public static int Main(string[] args)
=> CommandLineApplication.Execute<Program>(args);
[Option(Description = "The subject")]
public string Subject { get; } = "world";
[Option(ShortName = "n")]
public int Count { get; } = 1;
private void OnExecute()
{
for (var i = 0; i < Count; i++)
{
Console.WriteLine($"Hello {Subject}!");
}
}
}
Behind the scenes, this syntax is implemented with this code:
public static SetPropertyDelegate GetPropertySetter(PropertyInfo prop)
{
var setter = prop.GetSetMethod(nonPublic: true);
if (setter != null)
{
return (obj, value) => setter.Invoke(obj, new object?[] { value });
}
else
{
var backingField = prop.DeclaringType.GetField($"<{prop.Name}>k__BackingField", DeclaredOnlyLookup);
if (backingField == null)
{
throw new InvalidOperationException(
$"Could not find a way to set {prop.DeclaringType.FullName}.{prop.Name}. Try adding a private setter.");
}
return (obj, value) => backingField.SetValue(obj, value);
}
}
The practical value here is having the code express that the only way a value should be set is through a command line invocation. This is allowed: hello.exe -s world but this is not: Subject = "some other value";
Gleaning from the excellent answer above by #(Dan Solovay), we can now do something like this (made it easy to paste into LinqPad):
#nullable enable
void Main()
{
var model = new MyModel();
Console.WriteLine(model.Season);
var setter = GetSetterForProperty<MyModel, SeasonEnum>(x => x.Season);
setter?.Invoke(model, SeasonEnum.Summer);
Console.WriteLine(model.Season);
}
enum SeasonEnum
{
Unknown,
Spring,
Summer,
Autumn,
Winter
}
class MyModel
{
public SeasonEnum Season { get; }
}
private const BindingFlags DeclaredOnlyLookup = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;
private static Action<T, TValue>? GetSetterForProperty<T, TValue>(Expression<Func<T, TValue>> selector) where T : class
{
var expression = selector.Body;
var propertyInfo = expression.NodeType == ExpressionType.MemberAccess ? (PropertyInfo)((MemberExpression)expression).Member : null;
if (propertyInfo is null)
{
return null;
}
var setter = GetPropertySetter(propertyInfo);
return setter;
static Action<T, TValue> GetPropertySetter(PropertyInfo prop)
{
var setter = prop.GetSetMethod(nonPublic: true);
if (setter is not null)
{
return (obj, value) => setter.Invoke(obj, new object?[] { value });
}
var backingField = prop.DeclaringType?.GetField($"<{prop.Name}>k__BackingField", DeclaredOnlyLookup);
if (backingField is null)
{
throw new InvalidOperationException($"Could not find a way to set {prop.DeclaringType?.FullName}.{prop.Name}. Try adding a private setter.");
}
return (obj, value) => backingField.SetValue(obj, value);
}
}
Could you use "propertyInfo" approach. See the example below:
With a class like this:
public class MyClass {
public string MyAttribute{ get; } // --> blocked attribute
}
Use this code to change property value:
var instanceOfMyClass = new MyClass();
typeof(MyClass).GetProperty("MyAttribute")?.SetValue(instanceOfMyClass , "SomeValue");
or maybe you can write it a little more "elegant" using nameof.
typeof(MyClass).GetProperty(nameof(MyClass.MyAttribute))?.SetValue(instanceOfMyClass , "SomeValue");

How to decompose expression to satisfy generic property change method?

I have a base EF entity class which implements INotifyPropertyChanged.
The base property, Id is my example:
/// <summary>
/// Entity Id
/// </summary>
public int Id {
get { return id; }
set { SetValue<int>(() => (Id != value), (v) => id = v); } // < can this be simplified into a single call?
}
...where SetValue is defined:
protected void SetValue<TValue>(Expression<Func<bool>> evalExpr, Action<TValue> set) {
// Compile() returns a Func<bool>
var doSetValue = evalExpr.Compile();
if (doSetValue()) {
var expr = evalExpr.Body as BinaryExpression;
// this is not compiling - how do I decompose the expression to get what I need?
var propertyName = ((PropertyExpression)expr.Left).Name;
var assignValue = (TValue)((ConstantExpression)expr.Right).Value;
set(assignValue);
_propertyChangedHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
All samples I can find are expecting parameters. I prefer that the setter (SetValue call) is as simple as possible - i.e., is there a way to reduce the input parameter to 1?
You should change
var propertyName = ((PropertyExpression)expr.Left).Name;
to
var propertyName = ((MemberExpression)expr.Left).Member.Name;
and your code compiles, but what you are doing is not optimal and trustful at all. And you'll get an InvalidCastException!
Compiling an Expression<T> on every call is not optimal, and, how can you tell that the user passes the lambda to the method like:
() => (Id != value)
and not
() => (id != value) // using the field instead of property
or
() => (value != Id) // passing the property as the second operand
?
Also, value in your expression is not a ConstantExpression. The value itself is just a local variable to the set part of the property, and when passed to a lambda expression, is promoted to a class field (the value is captured - see here for more information). So what you have is a MemberExpression on both sides.
I highly recommend using this approach if you can't use .NET 4.5 ([CallerMemberName]):
public class EntityBase : INotifyPropertyChanged
{
protected virtual void OnPropertyChanged(string propName)
{
var h = PropertyChanged;
if (h != null)
h(this, new PropertyChangedEventArgs(propName));
}
public event PropertyChangedEventHandler PropertyChanged;
protected bool ChangeAndNofity<T>(ref T field, T value, Expression<Func<T>> memberExpression)
{
if (memberExpression == null)
{
throw new ArgumentNullException("memberExpression");
}
var body = memberExpression.Body as MemberExpression;
if (body == null)
{
throw new ArgumentException("Lambda must return a property.");
}
if (EqualityComparer<T>.Default.Equals(field, value))
{
return false;
}
field = value;
OnPropertyChanged(body.Member.Name);
return true;
}
}
Using it is simple:
public class Person : EntityBase
{
private int _id;
public int Id
{
get { return _id; }
set { ChangeAndNofity(ref _id, value, () => Id); }
}
}
There are various options that are simpler than what you've got (here are a few in rough order of how well I like each one):
Fody/PropertyChanged - This is a free, automatic code weaver that runs at compile time to automagically implement INotifyPropertyChanged on the properties of the classes you choose. No assemblies required at runtime.
INotifyPropertyChanged, The .NET 4.5 Way – Revisited
PostSharp - Automatically implementing INotifyPropertyChanged
INotifyPropertyChanged Interface documentation's code sample
Here's the core code snippet from "The .NET 4.5 Way":
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
Used like:
/// <summary>
/// Entity Id
/// </summary>
public int Id {
get { return id; }
set { SetValue(ref id, value); }
}

Class member as a first-class object

I was wondering if there is something in c# to be able to pass a member of a class to another function that will use this member to get a value. So get a value of a field determined only which one at runtime. Something like in other languages (PHP at least I think) that you can do
a.b = "something"
but also
a["b"] = "something";
edit: actually not so good an example since a string is used, sorry
For clarity an example of what I'd like to be able to do:
class A
{
int x;
int y;
}
void somethingsomething<T>(T class, SomeMagicFieldClass f)
{
dosomethingwith(somemethodthatgivesmethevalueoffield(class, f));
}
Where then I can call the method like this:
A a = new A();
somethingsomething(a, A.x); //hypothetical notation
somethingsomething(a, A.y);
I now have something similar where I do:
somethingsomething(a, "x");
somethingsomething(a, "y");
I then go find the field using introspection API (also trying GetProperty)
MemberInfo memberInfo = item.GetType().GetField(fieldName);
This works but the disadvantage is that the fields passed as a string won't get updated when "refactoring" fieldnames in visual studio, so I was thinking maybe there exists something like this in c# that would get refactored automatically when changing field names?
Thanks a lot for reading this boring question
Your example looks a lot like a LINQ key selector, in that form it would look like:
A a = new A();
somethingsomething(a, p => p.x);
You can do some nice refactor-friendly things with LINQ Expressions. Here is a snippet of utilty code I used for such occasions. It allows you to get the Name, Type and Value of a property (it won't work with fields without modifications). There's also a setter for the value.
public static void Main(string[] args) {
var test = new { Test1 = 42, Test2 = "123", Test3 = 3.14195 };
somethingSomething(test, t => t.Test1);
somethingSomething(test, t => t.Test2);
somethingSomething(test, t => t.Test3);
}
static void somethingSomething<TObj,TProperty>(TObj obj, Expression<Func<TObj,TProperty>> expr) {
var accessor = GetMemberAccessor(expr, obj);
String name = accessor.Name;
TProperty value = accessor.Value;
String typeName = accessor.Type.Name;
Console.WriteLine("{0} = {1} ({2})", name, value, typeName);
}
The output of that would be:
Test1 = 42 (Int32)
Test2 = 123 (String)
Test3 = 3.14195 (Double)
To make this work, I used the following helper function and class:
public static MemberAccessor<TReturn> GetMemberAccessor<TObj,TReturn>(Expression<Func<TObj, TReturn>> expr, TObj tar) {
var body = expr.Body;
MemberExpression memberExpression = null;
if (body is UnaryExpression) {
var ue = (UnaryExpression)body;
memberExpression = (MemberExpression)ue.Operand;
} else if (body is MemberExpression)
memberExpression = (MemberExpression)body;
else
throw new NotImplementedException("can't get MemberExpression");
String name = memberExpression.Member.Name;
return new MemberAccessor<TReturn>(tar, name);
}
public class MemberAccessor<T> {
private readonly PropertyDescriptor propertyDesc;
private readonly Object target;
public MemberAccessor(Object target, String propertyName) {
this.target = target;
this.propertyDesc = TypeDescriptor.GetProperties(target)[propertyName];
}
public String Name {
get { return propertyDesc.Name; }
}
public Type Type {
get { return propertyDesc.PropertyType; }
}
public T Value {
get { return (T)Convert.ChangeType(propertyDesc.GetValue(target), typeof(T)); }
set { propertyDesc.SetValue(target, value); }
}
}
Mr. Plunkett is correct; a dynamic type will do the job. Luckily, the .NET 4 team included a handy object called the ExpandoObject that solves that for you.
You asked how to
pass a member of a class to another
function that will use this member to
get a value
You can usedelegates for this
class A
{
public string aField;
public string aProperty{get{return "someval";}}
public string aMemberFunction(){return "someval";}
}
void get_a_value(Func<string> func)
{
string theValue = func();
}
// use it:
A a = new A();
get_a_value( () => a.aField);
get_a_value( () => a.aProperty);
get_a_value( () => a.aMemberFunction());
What you don't get this way, of course, is a separation of parameters for the memberfunction and the object you are passing.

Duck Typing DynamicObject derivate

I wrote a class that allows a derivate to specify which of its properties can be lazy loaded. The code is:
public abstract class SelfHydratingEntity<T> : DynamicObject where T : class {
private readonly Dictionary<string, LoadableBackingField> fields;
public SelfHydratingEntity(T original) {
this.Original = original;
this.fields = this.GetBackingFields().ToDictionary(f => f.Name);
}
public T Original { get; private set; }
protected virtual IEnumerable<LoadableBackingField> GetBackingFields() {
yield break;
}
public override bool TryGetMember(GetMemberBinder binder, out object result) {
LoadableBackingField field;
if (this.fields.TryGetValue(binder.Name, out field)) {
result = field.GetValue();
return true;
} else {
var getter = PropertyAccessor.GetGetter(this.Original.GetType(), binder.Name);
result = getter(this.Original);
return true;
}
}
public override bool TrySetMember(SetMemberBinder binder, object value) {
LoadableBackingField field;
if (this.fields.TryGetValue(binder.Name, out field)) {
field.SetValue(value);
return true;
} else {
var setter = PropertyAccessor.GetSetter(this.Original.GetType(), binder.Name);
setter(this.Original, value);
return true;
}
}
}
And a derivate class:
public class SelfHydratingPerson : SelfHydratingEntity<IPerson> {
private readonly IDataRepository dataRepository;
public SelfHydratingDerivate(IDataRepository dataRepository, IPerson person)
: base(person) {
this.dataRepository = dataRepository
}
protected override IEnumerable<LoadableBackingField> GetBackingFields() {
yield return new LoadableBackingField("Address", () => this.dataRepository.Addresses.Get(this.Original.AddressID));
}
}
This works perfectly fine for getting and settings property values, but I get a either a RuntimeBinderException when I implicitly cast or an InvalidCastException with an explicitly cast SelfHydratingEntity back to T.
I know that you can override the DynamicObject.TryConvert method, but I'm wondering what exactly to put in this method. I've read a lot about duck typing today, and have tried out several libraries, but none of them work for this particular scenario. All of the libraries I've tried today generate a wrapper class using Reflection.Emit that makes calls to "get_" and "set_" methods and naturally use reflection to find these methods on the wrapped instance. SelfHydratingEntity of course doesn't have the "get_" and "set_" methods defined.
So, I'm wondering if this kind of thing is even possible. Is there any way to cast an instance of SelfHydratingEntity to T? I'm looking for something like this:
var original = GetOriginalPerson();
dynamic person = new SelfHydratingPerson(new DataRepository(), original);
string name = person.Name; // Gets property value on original
var address = person.Address; // Gets property value using LoadableBackingField registration
var iPerson = (IPerson)person;
- or -
var iPerson = DuckType.As<IPerson>(person);
Have you seen this Duck Typing project. It looks pretty good. I have just found a great example from Mauricio. It uses the Windsor Castle dynamic proxy to intercept method calls
Using the code from Mauricio the following code works like a dream
class Program
{
static void Main(string[] args)
{
dynamic person = new { Name = "Peter" };
var p = DuckType.As<IPerson>(person);
Console.WriteLine(p.Name);
}
}
public interface IPerson
{
string Name { get; set; }
}
public static class DuckType
{
private static readonly ProxyGenerator generator = new ProxyGenerator();
public static T As<T>(object o)
{
return generator.CreateInterfaceProxyWithoutTarget<T>(new DuckTypingInterceptor(o));
}
}
public class DuckTypingInterceptor : IInterceptor
{
private readonly object target;
public DuckTypingInterceptor(object target)
{
this.target = target;
}
public void Intercept(IInvocation invocation)
{
var methods = target.GetType().GetMethods()
.Where(m => m.Name == invocation.Method.Name)
.Where(m => m.GetParameters().Length == invocation.Arguments.Length)
.ToList();
if (methods.Count > 1)
throw new ApplicationException(string.Format("Ambiguous method match for '{0}'", invocation.Method.Name));
if (methods.Count == 0)
throw new ApplicationException(string.Format("No method '{0}' found", invocation.Method.Name));
var method = methods[0];
if (invocation.GenericArguments != null && invocation.GenericArguments.Length > 0)
method = method.MakeGenericMethod(invocation.GenericArguments);
invocation.ReturnValue = method.Invoke(target, invocation.Arguments);
}
}
impromptu-interface
https://github.com/ekonbenefits/impromptu-interface
Can static cast interfaces onto objects derived from DynamicObject.

Categories