-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Add new GetValue method to System.Reflection #19484
Comments
Interesting, I didn't realize you can reassign values inside boxes. If this is going to work with properties, I think it should be made completely general and work with any method that returns a value. And a couple questions:
|
FieldInfo.GetValue/SetValue are part of reflection invoke APIs. All current reflection invoke APIs have a general problem that they expected everything boxed. It is not just a performance problem mentioned here, but also a functional deficiency problem. Among other things, it means that it is not possible to write high fidelity IL interpreter using reflection. Let's say that you would like to interpret If we were to add a new reflection invoke APIs, we should aim to solve both of these problems. CoreRT has internal Internal.Runtime.CallInterceptor.MakeDynamicCall that is basically a box-free version of the MemberInfo.Invoke. It would need to be cleaned up and argument validation added for it to become good public API. |
Another thing to consider would be finding a replacement for |
While that's true, it's rarely the case that the storage locations of the objects or values being passed by reference are truly in the The expression interpreter runs into a number of other issues as well. For example, we can't pass Having boxes passed to reflection APIs has also led to behavioral changes in the expression interpreter compared to the compiler, such as https://github.com/dotnet/corefx/issues/13007 (though one can argue the interpreter is doing the right thing in this case, merely by accident). |
Looking through available features (to avoid major changes in FX/CLR) I've stumbled upon arglist. C# have this undocumented keyword that is supported by CLR which allow for passing varying number of arguments and the use looks like this:
As you can see, you can even pass float as a ref. From what I can see by disassembling a code (x86-64), values are passed clean, without any boxing. New ReflectionAPI could utilize this to have both: box free GetValue as in Of course there would be a need to make a syntax nicer than a keyword __arglist, also, I'm not sure how this API would work with other languages. There is also a limit on how __arglist can be used. Currently ArgIterator, to read a value/argument, returns |
Problem shown in #19094 cannot be fixed by removing boxing. Boxing ensures that value types are valid during delegate invocation. In given example, variable o is valid only when delegate is created. Actual call to function f can occur much later when variable o is no longer available, thus copy must be made. |
|
The LocalVariableSet type used by MakeDynamicCall is very close to TypedReference. It is basically array of TypedReferences. It may be interesting to bring TypedReference back and making it useful, instead of creating a new same thing under a different name. |
Is there a specific reason why Another solution would be use of new Tuples feature.
I don't know the implementation details of the tuples, but afaik tuple is a struct that still needs to be boxed but we're boxing one struct instead of N-arguments and N-out-results. This wouldn't solve most of the problems, but GetValue could also use tuple to return a value (again, tuple is boxed only once and can be reused), could solve problems with properly resolving |
TypedReference has not been very useful: C# does not have a first class support for it, it requires non-trivial custom logic in the codegenerator and runtime, not all .NET runtimes support it. It is missing some methods that would make it useful and easy to work with, e.g. it should have a method to cast the value to strong typed value, like The non-boxing reflection method to get the value can then be:
I do not think Tuples are a good fit for this. |
I have opened dotnet/corefx#14088 to bring back TypedReference that should be the first on this. |
I think it should support I think GameObject obj;
FieldInfo fi = typeof(obj).GetField("Transform");
ref Matrix4x4 transform = fi.GetRefValue<Matrix4x4>(ref obj);
// ref transform live in this stack
transform.M43 = 1; // obj.Transform.M43 = 1 Also, Matrix4x4 matrix;
MethodInfo mi = typeof(matrix).GetMethod("RotateY");
RefAction<Matrix4x4,float> rotate = mi.GetRefAction<Matrix4x4,float>();
rotate(ref matrix,90); // Internally call matrix.RotateY(90);
////
namespace System
{
public delegate void RefAction<T>(ref T obj);
public delegate void RefAction<T,P1>(ref T obj,P1 param1);
public delegate void RefAction<T,P1...>(ref T obj,P1 param1...);
public delegate R RefFunc<T,R>(ref T obj);
public delegate R RefFunc<T,P1>(ref T obj,P1 param1);
public delegate R RefFunc<T,P1...>(ref T obj,P1 param1...);
public delegate ref R RefFuncRef<T,R>(ref T obj);
public delegate ref R RefFuncRef<T,P1>(ref T obj,P1 param1);
public delegate ref R RefFuncRef<T,P1...>(ref T obj,P1 param1...);
}
class System.Reflection.MethodInfo
{
public RefAction<T> GetRefAction<T,P>(P param) where T : class
{
// return void action(ref T t,param) that wrap void t.action(param);
}
} |
The implementation that returns TypedReference is more efficient and more powerful. It is more efficient because of it does not suffer from generic code bloat. It is more powerful because of you can easily add a strong typed generic convenience wrapper extension method around it that just returns ref (this wrapper can be part of the API proposal too); but the other direction is not possible. |
There are existing methods that do that like |
@jkotas You are right that it more powerful, which, at the same time, overkill. It not safe to pass as you say
So I want to trade off here. We can live with generic in the most case |
Also with The point here is to avoid bringing back |
TypedReference is coming back for other reasons. It is used in non-trivial percentage of .NET libraries. See dotnet/standard#20. |
There is nothing inherently unsafe about it. The safety rules for TypedReference are exactly same as the rules for |
@jkotas any I mean void DoSomething(ref int i)
{
i++; // normally fine
Action plusplus = () => i++; // error, cannot access ref
} is it the same for this? void DoSomething(TypedReference tr)
{
Action action = () => {
// would it error if we use tr here?
};
} |
I quite suspect that most of those could be replace with I said "not bringing it back" is misused, I was actually mean not encourage to using it more in the future or make any new library rely on it. It fine to have it support like |
TypedReference was in .NET since forever. byref-like type safety rules has been always enforced for it by the C# compiler. You example works as expected today - there is no extra work required to make it work. |
It is not possible to build things like good high fidelity IL interpreter on top of reflection without having the TypedReference-based reflection. I think it is important that any reflection APIs give you full power. We do not need yet another partial solution. |
@jkotas Well, that's why I propose Could you please guide me what |
If you are building things like IL interpreter, the actual types are not known. You need a type that can store any value that can appear as method argument or on IL stack. TypedReference is such type. |
OK, I quite get some image for the reason. So now I would like to agree with you Thank you very much for explanation 2 last things I concern is |
|
Thank you very much |
So actually what I should want is |
The ability to assign to a field reference obtained via reflection with equal performance to setting a field directly would be amazing. I could stop emitting IL for this. |
Provided TypedReference gets all the features C# already has with the undocumented keywords, it would be the most feasible way to pass around a simple reference to a field, without having to care about its type. Typed references can already be passed around as safely as normal typed references. Copying the value to an already-boxed instance sounds interesting, but I'd like to point out that ECMA-335 specifies the return type of the unbox instruction (akin to this behaviour) to be a controlled-mutability managed pointer. Although the CLR, as far as I know, makes no attempts in verifying the mutability of the pointer, while the similar behaviour of the Invoke method is one thing, the specification implies that at least it shouldn't be encouraged. |
Related proposal: dotnet/corefx#24390 |
This is a long range cross cutting feature (runtime/C#/framework changes.) |
Faster |
@GrabYourPitchforks Where exactly did this proposal end up? I'm still looking for a non-boxing solution similar to this one, but didn't see where #23716 was either. |
Assuming #45152 supersedes this. |
This new method would accept an "object" to which a Value could be copied instead of being unnecessarily boxed. Example of use would look like this:
Boxing of the value would occur only once. This would eliminate unnecessarily memory allocations in situations when GetValue is called many times. When method is used on a class types, copying would not occur and stored object would be returned instead.
One of the uses of GetValue method is User Interface based on MVVM (Model View Viewmodel) where values used to control an UI are pulled directly from the data. In realtime applications (like games) this boxing makes hundreds if not thousands of unnecessary memory allocations every second.
Another use of GetValue is serialization, and having ability to serialize hundreds of values without the need of boxing/unboxing, would greatly improve memory usage and performace.
There was also an idea https://github.com/dotnet/coreclr/issues/8277 that this new GetValue method would take a form of generic method, but I think this would limit the use as one of the advantages of working with System.Object is this abstraction that makes some parts of the code simpler.
So, ideally this new method should remove the unnecessary boxing while keeping convenience of System.Object. Additionally please bear in mind this should work with both FieldInfo and PropertyInfo, as well as give option to get values from nested Value as in following example:
(note intermixing fields with property)
The text was updated successfully, but these errors were encountered: