Replace object instance with another in C#
Solution 1
Since an object state is defined by field values, you can copy memory, containing field values, from one object to another, effectively "replacing" it:
public static void Replace<T>(T x, T y)
where T : class
{
// replaces 'x' with 'y'
if(x == null) throw new ArgumentNullException("x");
if(y == null) throw new ArgumentNullException("y");
var size = Marshal.SizeOf(typeof(T));
var ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(y, ptr, false);
Marshal.PtrToStructure(ptr, x);
Marshal.FreeHGlobal(ptr);
}
Note that this code requires [StructLayout(LayoutKind.Sequential)]
(or LayoutKind.Explicit
) attribute defined for a class.
Solution 2
You could do that if you embed your object into another one that is used to access the object.
class ObjectReference<T>
where T : new()
{
private T _obj = new T();
public void CreateNewObject()
{
_obj = new T();
}
public T Value { get return _obj; }
}
Now you can create multiple references to an object of type ObjectReference
and only change the local object. The "real" object would be accessed through the Value
property
A slightly different approach is that you create a wrapper that implements the same interface as your "real" object, thus making this wrapping transparent.
interface ISomeInterface
{
string PropertyA { get; set }
void MethodB (int x);
}
class TheRealObject : ISomeInterface
{
public string PropertyA { get; set }
public void MethodB (int x)
{
Console.WriteLine(x);
}
}
class Wrapper : ISomeInterface
{
TheRealObject _obj = new TheRealObject();
public string PropertyA
{
get { return _obj.PropertyA; }
set { _obj.PropertyA = value; }
}
public void MethodB (int x)
{
_obj.MethodB(x);
}
public void CreateNewObject()
{
_obj = new TheRealObject();
}
}
Now the wrapper can be used as if it was the "real" object. You could also pass an initial instance of the "real" object in the wrapper's constructor and remove the initializer of _obj
.
Solution 3
No, that's not possible.
To actually change all references to an object, you would have to freeze all threads in the process, and access their register sets and stack. That's what the garbage collector does, but it's not possible for regular code.
What the method most likely does is to make a deep copy of one object onto the other.
Solution 4
If it is a custom Class you want to reference, i think you can have all the references point to a Fake Reference...
- create your class (A)
- create your class Interface (IA)
- Create a wrapper class based on your interface which just passes all calls to a contained object (AC)
I Added a Assignment operator so i have all A Objects as ACs.
class AC:IA
{
IA ref;
AC(IA ref)
{
this.ref = ref;
}
public void ChangeReference(IA newRef) { ref = newRef;}
public static operator = (IA assignedObj)
{
return (assignedObject is AC) ? assignedObject : new AC(assignedObj);
}
// implementation of all methods in A
public override string ToString() { return ref.ToString(); }
...
}
Now if you want, you can use the ChangeReference method to switch all to the new Reference..
in C++ you would use Reference to Reference
Best of luck
Lea Hayes
I have been fascinated by software and video games since a young age when I was given my first computer, a Dragon 32. Since then I have experimented with numerous methods of development ranging from point-and-click type packages to C++. I soon realized that software development was what I wanted to do. Having invested a lot of time into programming with various languages and technologies I now find it quite easy to pickup new ideas and methodologies. I relish learning new ideas and concepts. Throughout my life I have dabbled in game and engine development. I was awarded a first for the degree "BEng Games and Entertainment Systems Software Engineering" at the University of Greenwich. It was good to finally experience video games from a more professional perspective. Due to various family difficulties I was unable to immediately pursue any sort of software development career. This didn't stop me from dabbling though! Since then I formed a company to focus upon client projects. Up until now the company has primarily dealt with website design and development. I have since decided that it would be fun to go back to my roots and develop games and tools that other developers can use for their games. We have recently released our first game on iPhone/iPad called "Munchy Bunny!" (see: View in app store). We hope to expand the game and release to additional platforms. Also, check out our tile system extension for Unity! (see: View product info)
Updated on June 19, 2022Comments
-
Lea Hayes almost 2 years
In this question I would like to find out if and how this is possible. This technique would seem extremely bad practice but it seems that the API (UnityEditor) that I am using, is doing something like this and I am just curious.
If there are multiple references to the same object, is it possible to instantiate a new object into the same memory slot so that all previous references point to the new object?
I figured out that the only feasible way to do so is by using unmanaged C++. Essentially the following is happening:
// Original prefab GameObject prefab = x; prefab.tag = "Untagged"; // A copy of the original prefab GameObject prefabCopy = PrefabUtility.InstantiatePrefab(prefab) as GameObject; prefabCopy.tag = "EditorOnly"; // Change from initial value "Untagged" Debug.Log(prefab.tag); // "Untagged" - expected Debug.Log(prefabCopy.tag); // "EditorOnly" - expected // Replace contents of prefab file with `prefabCopy` PrefabUtility.ReplacePrefab(prefabCopy, prefab); // Destroy the copy DestroyImmediate(prefabCopy); Debug.Log(prefab.tag); // "EditorOnly" - whoa?
Some how
prefab
is now pointing to a different object?Note: Bear in mind that Unity is built on top of the Mono flavour of .NET
-
Lea Hayes almost 12 yearsI think that this is the most likely scenario. Though I believe that
UnityEngine.Object
's are not thread safe. -
Lea Hayes almost 12 years
UnityEngine.Object
has[StructLayout (LayoutKind.Sequential)]
-
Lea Hayes almost 12 yearsUnity overloads all equality operators which do unusual things like compare positive with
null
after being destroyed. Is there a way withMarshal
to display a string representation of a pointer? I would like to compare the reference ofprefab
with the return value ofPrefabUtility.ReplacePrefab
without Unity being able to perform any sort of black magic. -
max almost 12 yearsYou can get address of some objects using
var gch = GCHandle.Alloc(obj, GCHandleType.Pinned); var address = gch.AddrOfPinnedObject(); gch.Free()
, but it won't work for most object types. I think that is as close to the object address as you can get. Anyway, I don't think there is a chance that something is changing references in your code instead of object instances (except for garbage collector of course). -
Jerdak almost 12 yearsThey are not and he is right. In fact I believe the documentation explicitly states that it is making a copy, not passing a reference.
-
Lea Hayes almost 12 yearsI was hoping to deduce whether the updated prefab reference was the same as the original as this would imply that the object is being reused. Unfortunately this did not work for this object type
ArgumentException: Object contains non-primitive or non-blittable data.
Cheers :-) -
Lea Hayes almost 12 yearsI have accepted this answer because I think that it is the most likely especially given that the
StructLayout
attribute is present. -
toughQuestions over 3 yearsWhat happens then if your class sets a property to another object (eg. MyProperty = someOtherInstantiatedObject). Now, if MyProperty is modified, why do I not see the changes in someOtherInstantiatedObject? How would you do it? Thanks
-
Olivier Jacot-Descombes over 3 years@toughQuestions: When
MyProperty
contains a reference tosomeOtherInstantiatedObject
and you modifyMyProperty
itself, it will point to yet a different object or tonull
but it does not modifysomeOtherInstantiatedObject
. But if you modifyMyProperty.SomeProperty
, this changes a property insidesomeOtherInstantiatedObject
. You must be aware that classes are reference types. A variable of such a type always either contains a reference ornull
. See also my answer to Setting a type reference type to null doesn't affect copied type?. -
toughQuestions over 3 yearsThanks for the link, it's pretty clear :-) So, how could I solve my problem? Is the only way to directly use someOtherInstantiatedObject as there is no real way to "rename" it as MyProperty?
-
Olivier Jacot-Descombes over 3 yearsPost a question to SO, where you show the relevant code and explain your problem in detail. From this brief comments a have difficulties to understand your problem.