Replace object instance with another in C#

17,754

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...

  1. create your class (A)
  2. create your class Interface (IA)
  3. 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

Share:
17,754
Lea Hayes
Author by

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, 2022

Comments

  • Lea Hayes
    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
    Lea Hayes almost 12 years
    I think that this is the most likely scenario. Though I believe that UnityEngine.Object's are not thread safe.
  • Lea Hayes
    Lea Hayes almost 12 years
    UnityEngine.Object has [StructLayout (LayoutKind.Sequential)]
  • Lea Hayes
    Lea Hayes almost 12 years
    Unity overloads all equality operators which do unusual things like compare positive with null after being destroyed. Is there a way with Marshal to display a string representation of a pointer? I would like to compare the reference of prefab with the return value of PrefabUtility.ReplacePrefab without Unity being able to perform any sort of black magic.
  • max
    max almost 12 years
    You 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
    Jerdak almost 12 years
    They 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
    Lea Hayes almost 12 years
    I 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
    Lea Hayes almost 12 years
    I have accepted this answer because I think that it is the most likely especially given that the StructLayout attribute is present.
  • toughQuestions
    toughQuestions over 3 years
    What 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
    Olivier Jacot-Descombes over 3 years
    @toughQuestions: When MyProperty contains a reference to someOtherInstantiatedObject and you modify MyProperty itself, it will point to yet a different object or to null but it does not modify someOtherInstantiatedObject. But if you modify MyProperty.SomeProperty, this changes a property inside someOtherInstantiatedObject. You must be aware that classes are reference types. A variable of such a type always either contains a reference or null. See also my answer to Setting a type reference type to null doesn't affect copied type?.
  • toughQuestions
    toughQuestions over 3 years
    Thanks 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
    Olivier Jacot-Descombes over 3 years
    Post 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.