|
PropertyAccessor fails when trying to dynamically read a property that overrides a virtual property in a base class. Error message: "Operation could destablize the runtime". Please advise.
|
|
|
|
|
If I wanted to use this to access many different classes (all of which use reflection), should I create a separate class or somehow add this to the base class?
What I am trying to ask is best possible strategy
Socket Rocket
|
|
|
|
|
Hi,
Take a look on my code. Its use generics to convert parameters and return values. Is limited to classes, structs will not work, interfaces should be work but is not implemented.
Is for read only but can be implemented to write too.
I believe that is faster because we don't need emit assemblies. What you think about?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace ConsoleApplication8
{
class Program
{
static void Main(string[] args)
{
var test = new { asValue = 17, asRef = "test" };
var reader = PropertyToDelegate.CreateDelegate(test, test.GetType().GetProperty("asValue").GetGetMethod());
Console.WriteLine(reader(test));
reader = PropertyToDelegate.CreateDelegate(test, test.GetType().GetProperty("asRef").GetGetMethod());
Console.WriteLine(reader(test));
// or
var type = test.GetType();
reader = PropertyToDelegate.CreateDelegate(type, type.GetProperty("asValue").GetGetMethod());
Console.WriteLine(reader(test));
reader = PropertyToDelegate.CreateDelegate(type, type.GetProperty("asRef").GetGetMethod());
Console.WriteLine(reader(test));
Console.ReadLine();
}
}
struct structTest
{
public int AsValue { get; set; }
public string AsRef { get; set; }
}
public delegate object Reader(object This);
static class PropertyToDelegate
{
public static Reader CreateDelegate<T>(T obj, MethodInfo method) where T : class
{
return PropertyToDelegate<T>.CreateDelegate(method);
}
public static Reader CreateDelegate(Type type, MethodInfo method)
{
return (Reader) typeof(PropertyToDelegate<>).MakeGenericType(type).GetMethod("CreateDelegate").Invoke(null, new object[] { method });
}
}
public static class PropertyToDelegate<T> where T : class
{
public delegate object TReader(T This);
static class BoxerForStructs<S> where S : struct
{
delegate S structReader(T obj);
public static TReader CreateDelegate(MethodInfo method)
{
var del = (structReader)Delegate.CreateDelegate(typeof(structReader), method);
return (T obj) => del(obj);
}
}
public static Reader CreateDelegate(MethodInfo method)
{
TReader del;
if (!method.ReturnType.IsValueType)
del = (TReader)Delegate.CreateDelegate(typeof(TReader), method);
else
del = (TReader)typeof(BoxerForStructs<>).MakeGenericType(typeof(T), method.ReturnType).GetMethod("CreateDelegate").Invoke(null, new object[] { method });
return (This) => del((T)This);
}
}
}
|
|
|
|
|
Hi James,
I know this is an old thread, but I thought I'll just make an observation for those who care to read reviews:
1. In SampleApp.cs, if I replace the Type.InvokeMember() calls with PropertyInfo.SetValue() and PropertyInfo.GetValue(), those durations are sliced to about 20% - 40% of the original time, provided you set the PropertyInfo once BEFORE the loop begins. I suppose it is because it doesn't have to lookup the property info 1,000,000 times as done in Type.InvokeMember() in the test app.
What this means is that the REFLECTION part of the test, which is the slowest, is not optimal.
2. If like me, you test the code provided by Tobi (Mr T#), and the generic implementation seems slower, it is only because he used 100,000,000 test cycles instead of 1,000,000 as used by James.
BTW, great article, and great code. Very useful. Thanks.
Regards,
Martin
|
|
|
|
|
Hi
As I understand from this code is that, this code calls the properties which are available in the class, I can't create a property. Correct me if i'm wrong.
And what if I want to create properties dynamically.
|
|
|
|
|
First of all, thanks a lot for this piece of work - It is very helpful in what I am trying to do - Pretty much exactly what i was looking for for a long time! I also extended it with a PropertyAccessorMgr class which auto-manages all property accessors that have been created and only creates a new one if it does not already exist.
Now is there an easy way to easily invoke methods or ctors dynamically? I'm not sure if this is the right place to ask but I couldn't seem to find any more info on this matter - And I figured if dynamic property invocation is working, why not methods or ctors? I'm confident someone here must have the answer
Edit: I realized 2 things that should be mentioned:
1. The containing class must be public (or -I assume- somehow accessible to the PropertyAccessor).
2. You MUST refer to the actual class of the object whose property you want to evaluate. You cannot use the PropertyAccessor of an underlying type or interface.
modified on Tuesday, August 5, 2008 5:08 PM
|
|
|
|
|
Quick variation based on lambda functions.
>----------------------------------------
class LambdaPropertyAccessor<T>
{
private Dictionary<string, Func<T, object>> propAccessors = new Dictionary<string, Func<T, object>>();
public LambdaPropertyAccessor()
{
Type type = typeof (T);
foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Public | BindingFlags.GetProperty
| BindingFlags.Instance))
{
if (!pi.CanRead)
continue;
ParameterExpression parameter = Expression.Parameter(type, "r");
Expression<Func<T, object>> lambda;
if (pi.PropertyType.IsValueType)
{
lambda = Expression.Lambda<Func<T, object>>(
Expression.TypeAs(Expression.Property(parameter, pi.Name), typeof(object)), parameter);
}
else
{
lambda = Expression.Lambda<Func<T, object>>(Expression.Property(parameter, pi.Name), parameter);
}
propAccessors.Add(pi.Name, lambda.Compile());
}
}
public object Get(T obj, string propName)
{
return propAccessors[propName](obj);
}
}
<----------------------------------------
1000000 property gets on integer...
Direct access ms: 15,625
PropertyAccessor (Reflection.Emit) ms: 78,125
Reflection ms: 4687,5
Lambda ms: 218,75
1000000 property gets on strings...
Direct access ms: 15,625
PropertyAccessor (Reflection.Emit) ms: 46,875
Reflection ms: 4765,625
Lambda ms: 203,125
|
|
|
|
|
This is pretty cool. How would you go about implementing a property setter with a lamda?
|
|
|
|
|
Property setter implementation will be a "statement lambda".
I'm not sure that it can be implemented using System.Linq.Expressions.
|
|
|
|
|
You can create dynamic setters.
Add the following code to the loop in the constructor:
<br />
ParameterExpression paramExpT = Expression.Parameter(typeof(T), "instance");<br />
ParameterExpression paramExpObj = Expression.Parameter(typeof(object), "obj");<br />
Action<T, object> setterLambda =<br />
Expression.Lambda<Action<T, object>>(<br />
Expression.Call(paramExpT, pi.GetSetMethod(),<br />
Expression.ConvertChecked(paramExpObj, pi.PropertyType)), paramExpT, paramExpObj)<br />
.Compile();<br />
propSetters.Add(pi.Name, setterLambda);<br />
where propSetters is a member variable:
<br />
private Dictionary<string, Action<T, object>> propSetters = new Dictionary<string, Action<T, object>>();<br />
You can then use a Set method to set the value of a property dynamically:
<br />
public void Set(T obj, string propName, object value)<br />
{<br />
propSetters[propName](obj, value);<br />
}<br />
|
|
|
|
|
I am creating new PropertyAccessor objects in a loop at the rate of about 100 per minute or so. My pagefile grows at a pretty brisk pace until it overwhelms my computer. This is in conjunction with table adapters for a data processing application. when I comment out (Property Adapter) pa.Set(...) in all 18 places, this no longer happens. I guess its the assemblies are being cached there. How do I remove them?
|
|
|
|
|
If you're not already doing so, I would recommend caching PropertyAccessors so that you never create more than one instance for a given property of a Type. Actually, without doing this, the PropertyAccessor class would be slower than reflection. If caching the PropertyAccessor is not feasible for your particular application, you can unload the cached property assemblies by creating the PropertyAccessors and performing your operations within a new AppDomain. When done with the processing, you can then unload that app domain and the assemblies created under it. However, this introduces code complexity and you'd have to use Remoting to communicate across the AppDomain boundaries.
|
|
|
|
|
Hi,
I have been using this code snippet & it worked perfect in .net 1.0 & 1.1. But ever since I ported to 2.0 there is a strange problem:
AssemblyBuilder newAssembly = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);<br />
ModuleBuilder newModule = newAssembly.DefineDynamicModule("Module");<br />
TypeBuilder myType = newModule.DefineType("Property", TypeAttributes.Public);
It throws exception in DefineType .
Say this code is executed 200 time. It will run fine 199 times but once it will throw exception. But it will surely throw exception everytime I run.
And it throws exception only when I debug using VS2005 not when I directly run.
Does anyone have any clue to this problem.
thanks,
Mandeep.
|
|
|
|
|
Hi there!
First of all - wow, James, that's excellent work! I have never used Emit before - and here it really makes so much sense!
I am using VS 2005 - so I thought, I could do a quick port to .NET 2.0, just to learn something about Emit. Turns out, it is tragically easy to implement it using generics. That is, implementing it using your great code. Seriously, it boils down to removing the casting stuff in your code.
But those easy modification give you an additional speed boost and actually cuts the get and set on value types in half! And there is still a significant speed gain on regular objects as well.
Have fun downloading GenericPropertyAccessor from my blog, plus an updated test app.
For those too lazy to click on the link in my blog - here is the direct link:
GenericPropertyAccessor as Zip
Have fun,
Tobi
P.S.: I have no idea, what to put in the header of these files?! How do I "clearly mark" that I changed the code but hugely depended on James' work? Is what I did ok, or should I change the header? Help!
---
Tobi + C# = T#
|
|
|
|
|
First, I thank James very much for the great work!
Hi Tobi, the Generic implemation is very nice.
I couldn't pass the property types dynamically to the GenericPropertyAccessor.
Eg:
The below code works fine for string type property.
GenericPropertyAccessor<propertyaccessortestobject, string=""> propertyAccessor =
new GenericPropertyAccessor<propertyaccessortestobject, <big="">string>("String");
but, I am using the above code in the iteration of a collection to display all the property values, so I should pass the property type (higleted) dynamically. any help?
LK.
|
|
|
|
|
Hey Leela,
I guess for your kind of situation you would have to use James' original PropertyAccessor. But actually I am not too sure if I understand the situation you are trying to solve.
Sorry.
Tobi
|
|
|
|
|
Hi,
great work, I've been using it and it works fine, but I have a problem: did you ever try accessing properties from a value type? If you try, for example, to get the property "Width" from a System.Drawing.Size, you don't get the value, but a "strange" number that seems to be a memory address... I'm totally ignorant about Emit and I'm not able to see where the problem is, did you verify the same problems and, if you did, do you have some hints about it?
Thanks, bye,
Wasp
|
|
|
|
|
Hi, this code will be really helpful to me but i also need getting name and value of all public properties. and i also don't have enough knowledge about IL coding. i'll give Type and BindingFlags as parameters instead of property name, and it will give me property info collection.
thanks for your help..
-- savas --
|
|
|
|
|
Given the code as is and the reflection API (you already know about BindingFlags) this should be a piece of cake. Unless you need help coding arrays/collections...?
modified 20-Jan-22 21:01pm.
|
|
|
|
|
How to make better (example of reflection) this situation on C#
// assembly1
class c1
{
public void method1()
{}
public int method2()
{
return 100;
}
}
// assembly2
class c2
{
c1 _c1 = new c1();
c3 _c3 = new _c3(_c1);
}
// assembly3
class c3(object _o)
{
_o.method1();
int y = 100;
y = y + _o.method2(); // 200
MessageBox.Show(y.ToString()); // 200
}
|
|
|
|
|
This is nice stuff, but I have one problem. When I try to set a null value I get a NullReferenceException when the type of the property is a ValueType. Is there a way to change the generated IL for the SetValue method so that it perfoms a null-check first and if so, set the value to the initial value of a ValueType (for example, 0 for ints)? I tried fixing it myself but the IL language is very difficult.
Thanks.
Pat
|
|
|
|
|
Rather than put this logic in the IL of the emitted property accessor, you could place it in the Set method of the concrete PropertyAccessor (see code below). It might even make sense to make the PropertyAccessor a base class with a protected abstract DefaultValue property. You could create a concrete implementation for each value type, but then you would also need a factory to create the appropriate property accessor...
public void Set(object target, object value)
{
if(mCanWrite)
{
if(this.mEmittedPropertyAccessor == null)
{
this.Init();
}
object newValue = value;
if(newValue == null && PropertyType.IsValueType)
{
newValue = DefaultValue;
}
this.mEmittedPropertyAccessor.Set(target, newValue);
}
else
{
throw new
PropertyAccessorException(string.Format("Property \"{0}\" does" +
" not have a set method.", mProperty));
}
}
James
|
|
|
|
|
James,
nice work. It was *almost* what i needed. I had the same use-case regarding public fields instead of properties. So I sat down and refactored a bit cheers, it refactored just fine (which compliments your style).
I factored out an (abstract) base class MemberAccessor, which has two descendants, FieldAccessor and PropertyAccessor, obviously.
The descendants sole task is to implement EmitSetter() and EmitGetter() (which are abstract methods on the baseclass.
I even sat down and refactored the tests. All tests relevant to both fields and methods have moved into a baseclass MemberAccessorTest, and the derived TestFixtures PropertyAccessorTest/FieldAccesorTest extend this with specific tests (e.g. there is no such thing as a WriteOnly field, and no such thing as a 'const' property).
Obviously there has been major find/replace Property->Member to keep the naming consistent with the purposes (e.g. IMemberAccessor instead of IPropertyAccessor).
The icing on the cake may be a factory method MemberAccessor.Make(...) which will instantiate either a PropertyAccessor or FieldAccessor by reflection.
If you are interested i will send/upload (?) the extended project 'FastDynamicMemberAccessor.proj'.
PS1. performance might have gone up by a chance to EmitAssembly that creates a 'sealed' class (gives compiler a huge optimization clue)
PS2. yes I use public fields for simple XmlSerializer-able classes
PS3. Needless to say, SampleApp runs without modification
PS4. The sharing of 36 test cases across the field/property variants has been achieved by implementing PropertyAccessorTestObject/FieldAccessorTestObject with a shared interface IMemberAccessorTestObject that interface is *only* used for test asserts, not for member access, of course
-- modified 20-Jan-22 21:01pm.
|
|
|
|
|
Did you publish your changes somewhere?
|
|
|
|
|
not yet. i may have them lying somewhere... i'll have to delve deep to find them now 
modified 20-Jan-22 21:01pm.
|
|
|
|
|