(C#) Как копировать классы по типу значения, а не по типу ссылки?

Возьмите следующий код:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MyClass
{
    public int myNumber;
}

public class Test : MonoBehaviour
{
    MyClass class1 = new MyClass();
    MyClass class2 = new MyClass();

    void Start()
    {
        class1.myNumber = 1;
        class2 = class1;
        class2.myNumber = 2;

        Debug.Log(class1.myNumber); //output: 2 (I'd want it to output 1!!!)
    }
}

Теперь, если я правильно понял, в C# классы являются ссылочным типом, а не типом значения, поэтому такое поведение желательно.
Однако что мне нужно сделать, чтобы этого не было? Что я мог сделать, чтобы сделать нормальное присвоение значений без «связывания» двух классов вместе?

Я понимаю, что могу заменить строку 18 на это:

class2.myNumber = class1.myNumber;

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

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


person man-teiv    schedule 15.04.2020    source источник
comment
Можно ли использовать struct?   -  person rhughes    schedule 15.04.2020
comment
Я не очень хорошо знаком с C#, но он очень похож на Java. Итак, docs.microsoft.com/en -нас/точка/апи/   -  person jiveturkey    schedule 15.04.2020
comment
Это называется Deep Copy или Clone. Вы можете посмотреть это. Быстрый и грязный способ - сериализоваться в json, а затем обратно в экземпляр класса.   -  person Crowcoder    schedule 15.04.2020
comment
@rhughes это интересно. В чем разница с классом? Ведет ли он себя так же, за исключением того, что он является типом значения, а не ссылочным типом?   -  person man-teiv    schedule 15.04.2020
comment
Если вы не используете глубокую копию, вы в основном просто указываете на тот же экземпляр, поскольку это ссылочный тип, также известный как адрес памяти. Поэтому, если вы измените значение для одного, оно изменит значения для другого. Deep Copy предотвращает это. Альтернатива называется поверхностной копией.   -  person MattE    schedule 15.04.2020
comment
Другим возможным решением может быть использование неизменяемых типов. Если объект не может измениться, ваша проблема решена. Без дополнительной информации трудно сказать, является ли этот вариант разумным подходом или нет.   -  person InBetween    schedule 15.04.2020
comment
@InBetween на самом деле, для моих скриптов один из двух классов не требует изменений во времени, а другой требует ... есть ли способ сделать только один неизменяемым?   -  person man-teiv    schedule 15.04.2020
comment
Если он не меняется, нет необходимости что-либо делать, даже если он не является неизменным, у вас нет проблемы, о которой вы спрашиваете. Что касается того, чтобы сделать тип неизменным или нет, это полностью зависит от того, как он реализован, это не вариант, который вы можете включить или выключить для любого данного типа или проекта. Если реализация не ваша, то нет способа сделать изменяемый тип неизменяемым; вам нужно спроектировать его таким образом.   -  person InBetween    schedule 15.04.2020


Ответы (3)


Выполните глубокое или поверхностное копирование в зависимости от ваших потребностей. https://docs.microsoft.com/en-us/dotnet/api/system.object.memberwiseclone?view=netframework-4.8

person jiveturkey    schedule 15.04.2020
comment
Спасибо @jiveturkey, я принял ваш ответ, потому что он правильный, на самом деле в примере сценария он работает. Но, ради всего святого, в моем основном сценарии этого нет! Я продолжу исследования, но я пойду в этом направлении. Просто чтобы убедиться, что если мой класс имеет только типы string, int и int[], могу ли я просто использовать ShallowCopy? - person man-teiv; 15.04.2020
comment
Я считаю, что это то, что вы хотите. Однако дважды проверьте строку. Это может быть объект, а не базовый тип. - person jiveturkey; 15.04.2020
comment
Кажется, я узнал свою проблему. По-видимому, массивы имеют ссылочный тип. Я пытаюсь повторно адаптировать метод DeepCopy, чтобы посмотреть, смогу ли я совместить его с ними. - person man-teiv; 15.04.2020

В C# есть что-то, называемое Struct, которое в основном представляет собой облегченную версию класса, который является типом значения. Если вам конкретно нужен тип значения, вы можете использовать структуры вместо классов.

Основные отличия заключаются в том, что структуры не нужно создавать с помощью «нового» назначения, они не могут наследоваться от другой структуры или класса и не могут использоваться в качестве основы для другого класса.

person MattE    schedule 15.04.2020
comment
Структуры могут по-прежнему иметь свойства ссылочного типа, поэтому простое использование Struct — это еще не весь ответ. - person Crowcoder; 15.04.2020
comment
@Crowcoder, не могли бы вы немного рассказать об этом? Какие свойства по-прежнему имеют ссылочный тип? - person man-teiv; 15.04.2020
comment
@Francesco Он говорит, что переход на структуру не является общим решением. Если структура содержит ссылку на изменяемый объект ссылочного типа, то изменение в этом объекте будет видно всем копиям данного типа значения независимо от структур, имеющих семантику копирования по значению. - person InBetween; 15.04.2020
comment
Между поведением классов и структур в .Net есть существенные различия. Изменение типа на struct просто для того, чтобы избежать того, как .Net обрабатывает ссылочные и значимые типы, не является хорошим дизайнерским решением. Прежде чем идти по этому пути, убедитесь, что вы полностью понимаете компромиссы, на которые идете. Начните читать здесь и продолжите с нескольких общих вопросов о классах и структурах C# в StackOverflow. - person Patrick Tucci; 15.04.2020

Как насчет этого?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MyClass
{
    public int myNumber;
}

public class Test : MonoBehaviour
{
    MyClass class1 = new MyClass();
    MyClass class2 = new MyClass();

    private void Start()
    {
        class1.myNumber = 1;
        class2 = GetClassCopy(class1);
        class2.myNumber = 2;
        Debug.Log(class1.myNumber); // output:1
    }

    private MyClass GetClassCopy(MyClass source)
    {
        MyClass result = new MyClass();
        result.myNumber = source.myNumber;
        return result;
    }
}
person Armin    schedule 15.04.2020