Замените экземпляр объекта другим в C#

в этом вопросе я хотел бы узнать, возможно ли и как это возможно. Эта техника кажется очень плохой практикой, но кажется, что API (UnityEditor), который я использую, делает что-то подобное, и мне просто любопытно.

если существует несколько ссылок на один и тот же объект, можно ли создать экземпляр нового объекта в том же слоте памяти, чтобы все предыдущие ссылки указывали на новый объект?

Я понял, что единственный возможный способ сделать это можно с помощью неуправляемого C++. По существу происходит следующее:

// 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?

кое-как prefab теперь указывает на другой объект?

Примечание: имейте в виду, что Unity построен поверх моно-вкуса .NET

4 ответов


поскольку состояние объекта определяется значениями полей, вы можете копировать память, содержащую значения полей, из одного объекта в другой, эффективно "заменяя"его:

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);
}

обратите внимание, что этот код требует [StructLayout(LayoutKind.Sequential)] (или LayoutKind.Explicit) атрибут, определенный для класса.


вы можете сделать это, если вы вставляете свой объект в другой, который используется для доступа к объекту.

class ObjectReference<T>
   where T : new()
{
    private T _obj = new T();

    public void CreateNewObject()
    {
        _obj = new T();
    }

    public T Value { get return _obj; }
}

теперь вы можете создать несколько ссылок на объект типа MyObjectReference и только изменить локальный объект. "Реальный" объект будет доступен через Value свойства

немного другой подход заключается в том, что вы создаете обертку, которая реализует тот же интерфейс, что и ваш "реальный" объект, таким образом, делая эту обертку прозрачный.

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();
    }
}

теперь обертку можно использовать, как если бы это был" реальный " объект. Вы также можете передать начальный экземпляр "реального" объекта в конструкторе оболочки и удалить инициализатор _obj.


нет, это невозможно.

чтобы фактически изменить все ссылки на объект, вам придется заморозить все потоки в процессе и получить доступ к их наборам регистров и стеку. Это то, что делает сборщик мусора, но это невозможно для обычного кода.

скорее всего, метод делает глубокую копию одного объекта на другой.


Если это пользовательский класс, на который вы хотите ссылаться, я думаю, что вы можете иметь все ссылки на поддельную ссылку...

  1. создайте свой класс (A)
  2. создайте свой интерфейс класса (IA)
  3. создайте класс-оболочку на основе вашего интерфейса, который просто передает все вызовы содержащемуся объекту (AC)

Я добавил оператор присваивания, поэтому у меня есть все объекты как 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(); }  
        ...  
}

теперь, если вы хотите, вы можете использовать Метод ChangeReference для переключения всех на новую ссылку..

в C++ Вы бы использовали ссылку на ссылку

удачи