The following code doesn't update the copy of a inside the array.
int a = null;
int[] numbers = new int[1];
numbers[0] = a;
a = 5; // 5
Console.WriteLine(numbers[0]); // null
Got a programming task requiring to set-up a structure of locations linked by portals between them which isn't possible by just listing the required connections. I'll get references to null that stay null even if I fill an entity later in the code.
Looking for keywords or techniques which might solve my issue.
You could have reference types instead of value types inside array, therefore updating the value of the inner object will also affect the array.
var tab = new MyClass[1];
var obj = new MyClass(5);
tab[0] = obj;
Console.WriteLine(tab[0].Value); // 5
tab[0].Value = 10;
Console.WriteLine(tab[0].Value); // 10
obj.Value = 15;
Console.WriteLine(tab[0].Value); // 15
public class MyClass
{
public MyClass(int value)
{
Value = value;
}
public int Value { get; set; }
}
integers is a value type, as such the actual value is copied. So there is never any 'instance' in your example code, only copies of the value.
You should probably wrap your value in a class, since classes are a reference type to get your desired behavior. This might be useful when you need to share some mutable between multiple components. You can also add an event that is raised whenever the value is changed to let any component that needs the value know that it might need to update something. For example:
public class MyChangeable<T>
{
private T value;
public MyChangeable(T value) => this.value = value;
public T Value
{
get => value;
set
{
this.value = value;
OnChanged(this, value);
}
}
public event EventHandler<T> OnChanged;
}
There is also ref return and ref locals that could do something like your example, but this is mostly intended to get better performance by avoiding copies of large structs, it is not as useful if you want to share values between components.
Arrays are reference types
var a = new int[1];
var numbers = new [] { a };
a[0] = 5;
Console.WriteLine(numbers[0][0]);
You just have to remember that you're one level deeper than you wanted to be/you need to stick a [0] on everything you wouldn't have stuck it on before. It's a bit of a hack, and I'd probably make a class for it like other answers recommend, but stuffing a value type in an array of size 1 can be a useful technique to quickly get reference type behavior out of a value type
ref locals may help you in your task, even if they have strict limitations due to the lifetime of the involved objects, so they could be not applicable as a general solution.
A small example based on your question can be as follows:
int[] array = new int[1];
ref int elem = ref array[0];
elem = 5;
Console.WriteLine(array[0]); // 5
This works not only with value types (including nullable types) but also with reference types.
This question already has answers here:
Sum up all the properties of a collection and dynamically assigned it to another object
(3 answers)
Why doesn't reflection set a property in a Struct?
(3 answers)
Closed 3 years ago.
So I have a struct like this:
public struct Attributes {
public int vitality;
public int intelligence;
public int dexterity;
public int agility;
}
And I use it like this:
Attributes a = new Attributes();
Attributes b = new Attributes();
And what I want to achieve is this:
Attributes c = new Attributes();
c = a + b;
I want this to give me the sum of these 4 variables I specified above of those two Attributess.
Inside the struct, I tried to have this:
public static Attributes operator +(Attributes x, Attributes y) {
PropertyInfo[] info = typeof(Attributes).GetType().GetProperties();
for (int i = 0; i < info.Length; i++) {
info[i].SetValue(x, (int) info[i].GetValue(x) + (int) info[i].GetValue(y), null);
}
return x;
}
This apparently doesn't work, giving me an error.
Can you guys help me about this? What could I do to achieve what I want? Thanks.
Just use the following:
public static Attributes operator +(Attributes x, Attributes y) {
return new Attributes
{
vitality = x.vitality + y.vitality,
intelligence = x.intelligence + y.intelligence,
dexterity = x.dexterity+ y.dexterity,
agility = x.agility + y.agility
};
}
If you don't have to, there's no need to be fancy and use reflection. It's a powerful tool but don't fall into the golden hammer fallacy. Only use it where truly necessary.
EDIT: if you really do want to use Reflection, this is a working version of your code:
public static Attributes operator +(Attributes x, Attributes y)
{
FieldInfo[] info = typeof(Attributes).GetFields();
object boxedResult = new Attributes();
foreach (FieldInfo fi in info)
{
fi.SetValue(boxedResult, (int)fi.GetValue(x) + (int)fi.GetValue(y));
}
return (Attributes)boxedResult;
}
I think it warrants some explanation for what changes I made:
I would consider it unusual if operator+ modified one of its operands, so I made it return a new Attributes struct instead.
You called typeof(Attributes).GetType() which basically took the type of Attributes and got the type of the type, which is definitely not what you want.
You were checking for property info, but Attributes does not have properties, only fields.
I explicitly boxed the Attributes struct before setting its fields. Boxing a struct makes a copy of it, and boxing happens when you take a value type (like any struct for example) and cast it to object. What happens is your value type (which lives on the stack) is put into a neat little reference-type box and stored on the heap, since only reference types can live on the heap. Actually, a copy of it is stored on the heap. So since SetValue takes an object parameter as the "target", the struct would be boxed every time, effectively taking your changes and applying them to a copy which is then promptly thrown away. By explicitly boxing it, I make all the changes on the same copy of your struct, and then returning that after unboxing it. This step would not be necessary if Attributes was a reference type.
I've seen this code which was used showing the reference value :
static void Main(string[] args)
{
string s1 = "ab";
string s2 = "a"+"b";
string s3 = new StringBuilder("a").Append("b").ToString();
Console.WriteLine(GetMemoryAddress(s1));
Console.WriteLine(GetMemoryAddress(s2));
Console.WriteLine(GetMemoryAddress(s3));
}
static IntPtr GetMemoryAddress(object s1)
{
unsafe
{
TypedReference tr = __makeref(s1);
IntPtr ptr = **(IntPtr**) (&tr);
return ptr;
}
}
Result (as expected):
(I know that string interning kicks in here, but that's not the question).
Question:
Although it seems that it does do the job,
Does using __makeref is this the right way of getting the reference value in c#?
Or are there any situations in which this ^ would fail ....?
Although it seems that it does do the job, Does using __makeref is this the right way of getting the reference value in c#?
There is no "right way" of doing this in C# - it isn't something you're meant to try and do, but: in terms of what it is doing - this is essentially relying on the internal layout of TypedReference and a type coercion; it'll work (as long as TypedReference doesn't change internally - for example the order of the Type and Value fields changes), but... it is nasty.
There is a more direct approach; in IL, you can convert from a managed pointer to an unmanaged pointer silently. Which means you can do something nasty like:
unsafe delegate void* RefToPointer(object obj);
static RefToPointer GetRef { get; } = MakeGetRef();
static RefToPointer MakeGetRef()
{
var dm = new DynamicMethod("evil", typeof(void*), new[] { typeof(object) });
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ret);
return (RefToPointer)dm.CreateDelegate(typeof(RefToPointer));
}
and now you can just do:
var ptr = new IntPtr(GetRef(o));
Console.WriteLine(ptr);
This is horrible, and you should never do it - and of course the GC can move things while you're not looking (or even while you are looking), but... it works.
Whether ref-emit is "better" than undocumented and unsupported language features like __makeref and type-coercion: is a matter of some debate. Hopefully purely academic debate!
Warning: This is merely an exercise for those whose are passionate about breaking stuff to understand their mechanics.
I was exploring the limits of what I could accomplish in C# and I wrote a ForceCast() function to perform a brute-force cast without any type checks. Never consider using this function in production code.
I wrote a class called Original and a struct called LikeOriginal, both with two integer variables. In Main() I created a new variable called orig and set it to a new instance of Original with a=7 and b=20. When orig is cast into LikeOriginal and stored in casted, the values of cG and dG become undefined, which is to be expected as LikeOriginal is a struct and class instances contain more metadata than struct instances thus causing memory layout mismatch.
Example Output:
Casted Original to LikeOriginal
1300246376, 542
1300246376, 542
added 3
Casted LikeOriginal back to Original
1300246379, 545
Notice, however, that when I call casted.Add(3) and cast back to Original and print the values of a and b, surprisingly they are successfully incremented by 3, and this has been repeatable.
What is confusing me is the fact that casting the class to the struct will cause cG and dG to map to class metadata, but when they are modified and cast back to a class, they map correctly with a and b.
Why is this the case?
The code used:
using System;
using System.Runtime.InteropServices;
namespace BreakingStuff {
public class Original {
public int a, b;
public Original(int a, int b)
{
this.a = a;
this.b = b;
}
public void Add(int val)
{
}
}
public struct LikeOriginal {
public int cG, dG;
public override string ToString() {
return cG + ", " + dG;
}
public void Add(int val) {
cG += val;
dG += val;
}
}
public static class Program {
public unsafe static void Main() {
Original orig = new Original(7, 20);
LikeOriginal casted = ForceCast<Original, LikeOriginal>(orig);
Console.WriteLine("Casted Original to LikeOriginal");
Console.WriteLine(casted.cG + ", " + casted.dG);
Console.WriteLine(casted.ToString());
casted.Add(3);
Console.WriteLine("added 3");
orig = ForceCast<LikeOriginal, Original>(casted);
Console.WriteLine("Casted LikeOriginal back to Original");
Console.WriteLine(orig.a + ", " + orig.b);
Console.ReadLine();
}
//performs a pointer cast but with the same memory layout.
private static unsafe TOut ForceCast<TIn, TOut>(this TIn input) {
GCHandle handle = GCHandle.Alloc(input);
TOut result = Read<TOut>(GCHandle.ToIntPtr(handle));
handle.Free();
return result;
}
private static unsafe T Read<T>(this IntPtr address) {
T obj = default(T);
if (address == IntPtr.Zero)
return obj;
TypedReference tr = __makeref(obj);
*(IntPtr*) (&tr) = address;
return __refvalue(tr, T);
}
}
}
€dit: Long story short: first create a ForceCast function that correctly handles both identity translations ForceCast<LikeOriginal, LikeOriginal> and ForceCast<Original, Original>, then you might have a chance to get actual conversions working
A working sample
By providing different codes for class->class (CC), class->struct (CS), struct->class (SC) and struct->struct (SS), using Nullable<T> as intermediate for structs, I got a working example:
// class -> class
private static unsafe TOut ForceCastCC<TIn, TOut>(TIn input)
where TIn : class
where TOut : class
{
var handle = __makeref(input);
return Read<TOut>(*(IntPtr*)(&handle));
}
// struct -> struct, require nullable types for in-out
private static unsafe TOut? ForceCastSS<TIn, TOut>(TIn? input)
where TIn : struct
where TOut : struct
{
var handle = __makeref(input);
return Read<TOut?>(*(IntPtr*)(&handle));
}
// class -> struct
private static unsafe TOut? ForceCastCS<TIn, TOut>(TIn input)
where TIn : class
where TOut : struct
{
var handle = __makeref(input);
// one extra de-reference of the input pointer
return Read<TOut?>(*(IntPtr*)*(IntPtr*)(&handle));
}
// struct -> class
private static unsafe TOut ForceCastSC<TIn, TOut>(TIn? input)
where TIn : struct
where TOut : class
{
// get a real pointer to the struct, so it can be turned into a reference type
var handle = GCHandle.Alloc(input);
var result = Read<TOut>(GCHandle.ToIntPtr(handle));
handle.Free();
return result;
}
Now use the appropriate function in your sample and handle the nullable types like the compiler demands:
Original orig = new Original(7, 20);
LikeOriginal casted = ForceCastCS<Original, LikeOriginal>(orig) ?? default(LikeOriginal);
Console.WriteLine("Casted Original to LikeOriginal");
Console.WriteLine(casted.cG + ", " + casted.dG);
Console.WriteLine(casted.ToString());
casted.Add(3);
Console.WriteLine("added 3");
orig = ForceCastSC<LikeOriginal, Original>(casted);
Console.WriteLine("Casted LikeOriginal back to Original");
Console.WriteLine(orig.a + ", " + orig.b);
Console.ReadLine();
For me, this returns the correct numbers at each point.
Details
Some details:
Basically, your problem is you treat a value type like a reference type...
Lets first look at the working case: LikeOriginal -> Original:
var h1 = GCHandle.Alloc(likeOriginal);
var ptr1 = GCHandle.ToIntPtr(h1);
This creates a pointer that points to the memory area of LikeOriginal (€dit: actually, not really exactly that memory area, see below)
var obj1 = default(Original);
TypedReference t1 = __makeref(obj1);
*(IntPtr*)(&t1) = ptr1;
This creates a reference (pointer) to Original with the value of a pointer, pointing to LikeOriginal
var original = __refvalue( t1,Original);
This turns the typed reference into a managed reference, pointing to the memory of LikeOriginal. All values of the starting likeOriginal object are retained.
Now lets analyze some intermediate case that should work, if your code would work bi-directional: LikeOriginal -> LikeOriginal:
var h2 = GCHandle.Alloc(likeOriginal);
var ptr2 = GCHandle.ToIntPtr(h2);
Again, we have a pointer that points to the memory area of LikeOriginal
var obj2 = default(LikeOriginal);
TypedReference t2 = __makeref(obj2);
Now here is the first hint of what is going wrong: __makeref(obj2) will create a reference to the LikeOriginal object, not to some separate area where the pointer is stored.
*(IntPtr*)(&t2) = ptr2;
ptr2 however, is a pointer to some reference value
var likeOriginal2 = __refvalue( t2,LikeOriginal);
Here we are, getting garbage because t2 would be supposed to be a direct reference to the object memory, instead of a reference to some pointer memory.
Following is some testcode I executed to get a better understanding of your approach and what goes wrong (some of it pretty structured, then some parts where I tried some additional things):
Original o1 = new Original(111, 222);
LikeOriginal o2 = new LikeOriginal { cG = 333, dG = 444 };
// get handles to the objects themselfes and to their individual properties
GCHandle h1 = GCHandle.Alloc(o1);
GCHandle h2 = GCHandle.Alloc(o1.a);
GCHandle h3 = GCHandle.Alloc(o1.b);
GCHandle h4 = GCHandle.Alloc(o2);
GCHandle h5 = GCHandle.Alloc(o2.cG);
GCHandle h6 = GCHandle.Alloc(o2.dG);
// get pointers from the handles, each pointer has an individual value
IntPtr i1 = GCHandle.ToIntPtr(h1);
IntPtr i2 = GCHandle.ToIntPtr(h2);
IntPtr i3 = GCHandle.ToIntPtr(h3);
IntPtr i4 = GCHandle.ToIntPtr(h4);
IntPtr i5 = GCHandle.ToIntPtr(h5);
IntPtr i6 = GCHandle.ToIntPtr(h6);
// get typed references for the objects and properties
TypedReference t1 = __makeref(o1);
TypedReference t2 = __makeref(o1.a);
TypedReference t3 = __makeref(o1.b);
TypedReference t4 = __makeref(o2);
TypedReference t5 = __makeref(o2.cG);
TypedReference t6 = __makeref(o2.dG);
// get the associated pointers
IntPtr j1 = *(IntPtr*)(&t1);
IntPtr j2 = *(IntPtr*)(&t2); // j1 != j2, because a class handle points to the pointer/reference memory
IntPtr j3 = *(IntPtr*)(&t3);
IntPtr j4 = *(IntPtr*)(&t4);
IntPtr j5 = *(IntPtr*)(&t5); // j4 == j5, because a struct handle points directly to the instance memory
IntPtr j6 = *(IntPtr*)(&t6);
// direct translate-back is working for all objects and properties
var r1 = __refvalue( t1,Original);
var r2 = __refvalue( t2,int);
var r3 = __refvalue( t3,int);
var r4 = __refvalue( t4,LikeOriginal);
var r5 = __refvalue( t5,int);
var r6 = __refvalue( t6,int);
// assigning the pointers that where inferred from the GCHandles
*(IntPtr*)(&t1) = i1;
*(IntPtr*)(&t2) = i2;
*(IntPtr*)(&t3) = i3;
*(IntPtr*)(&t4) = i4;
*(IntPtr*)(&t5) = i5;
*(IntPtr*)(&t6) = i6;
// translate back the changed references
var s1 = __refvalue( t1,Original); // Ok
// rest is garbage values!
var s2 = __refvalue( t2,int);
var s3 = __refvalue( t3,int);
var s4 = __refvalue( t4,LikeOriginal);
var s5 = __refvalue( t5,int);
var s6 = __refvalue( t6,int);
// a variation, primitively dereferencing the pointer to get to the actual memory
*(IntPtr*)(&t4) = *(IntPtr*)i4;
var s4_1 = __refvalue( t4,LikeOriginal); // partial result, getting { garbage, 333 } instead of { 333, 444 }
// prepare TypedReference for translation between Original and LikeOriginal
var obj1 = default(Original);
var obj2 = default(LikeOriginal);
TypedReference t7 = __makeref(obj1);
TypedReference t8 = __makeref(obj2);
// translate between Original and LikeOriginal
*(IntPtr*)(&t7) = i4; // From struct to class, the pointer aquired through GCHandle is apropriate
var s7 = __refvalue( t7,Original); // Ok
*(IntPtr*)(&t8) = *(IntPtr*)j1;
var s8 = __refvalue( t8,LikeOriginal); // Not Ok - Original has some value comming before its first member - getting { garbage, 111 } instead of { 111, 222 }
*(IntPtr*)(&t8) = j2;
var s9 = __refvalue( t8,LikeOriginal); // Ok by starting at the address of the first member
Conclusion: Going via GCHandle -> IntPtr is creating a pointer that is pointing to one memory location in front of the first member, no matter whether the starting point is a struct or a class. This results in a situation, where struct -> class or class -> class is working but class -> struct or struct -> struct is not working.
The only way I found for targeting structs is to get a pointer to their first member (which in case of an input struct equals the __makeref to the struct without going via GCHandle).
Here is how I see this situation. You have acted upon the reference to Original as if it were a reference to LikeOriginal. Critical point here is that you are invoking LikeOriginal.Add() method, the address of which is resolved statically during compile time.
This method, in turn, operates on a this reference which it implicitly receives. Therefore, it modifies values which are offset by 0 and by 4 bytes relative to this reference it has in its hands.
Since this experiment worked out, it indicates that the layouts of Original object and LikeOriginal struct are the same. I know that structs have flat layout, which makes them useful when allocating arrays of structs - there will be nothing inserted into the sequence of bytes representing flat content of structs. That is precisely what doesn't stand for classes - they need one reference which will be used to resolve virtual functions and type at run time.
Which reminds me to say that the lacking of this added reference is the core reason why structs do not support derivation - you wouldn't know whether you have a base or derived struct in a later call.
Anyway, back to the surprising fact that this code worked fine. I have been working with C++ compilers and I remember that they used to put the v-table pointer before actual data content of the object. In other words, this pointer used to point 4 bytes after actual address of the memory block allocated for that object. Maybe C# is doing the same, in which case this reference in a method invoked on Original points to a, just like the this reference in a method invoked on LikeOriginal points to cG.
I'm trying to understand persistent memory in C# and don't know why this code is not keeping the change made in one of the functions.
using System;
using System.Runtime.InteropServices;
public class Test2
{
public struct value
{
public int sz;
}
public static void Main(string[] args)
{
one foo1 = new one(one_full);
two foo2 = new two(two_full);
make_calls(foo1, foo2);
}
public delegate void one(IntPtr ctx);
public static void one_full(IntPtr ctx)
{
/* set sz and see if persists */
GCHandle gch = GCHandle.FromIntPtr(ctx);
value val = (value)gch.Target;
val.sz = 6;
Console.WriteLine("Changed sz to be 6");
}
public delegate void two(IntPtr ctx);
public static void two_full(IntPtr ctx)
{
GCHandle gch = GCHandle.FromIntPtr(ctx);
value val = (value)gch.Target;
Console.Write("sz is = ");
Console.WriteLine(val.sz);
}
public static void make_calls(one f_one, two f_two)
{
value test = new value();
test.sz = 0;
IntPtr ptr = GCHandle.ToIntPtr(GCHandle.Alloc(test));
f_one(ptr);
f_two(ptr);
}
}
I know it's missing the free at the end but that is just leading to messy memory management.... I'm looking for if someone can help me out and explain why sz does not stay as the value 6 for when the second function is called...
The output when ran is:
Changed sz to be 6
sz is = 0
It's all because value is a struct.
GCHandle.Alloc takes an object parameter, so if you pass a struct, it has to be boxed.
Later when you use
value val = (value)gch.Target;
it has to be unboxed, so a copy of it is stored in val. Any modifications you make later are made on the copy, not on the boxed struct.
It has nothing to do with GCHandle, it's how value types (structs) work in C#. That's why it's recommended to make value types immutable.
Becuase value is a sturct and structs are not reference types. when you have an instance of a struct and var b = instanceOfStruct then b is a new struct not a reference to instanceOfStruct. Chaning values in b does not reflect to instanceOfStruct.
In your code:
value val = (value)gch.Target;
will create a new instance of value struct which has same values as the structs that gch.Target point to it. Changing val does not change the struct behind gch.Target. The problem is because of a confusion between value types and reference types in C#. If you change value type to a class instead of struct then you will get the desired result. You can also use dynamic to modify the struct which is target of the handle:
dynamic val = gch.Target;
val.sz = 6;