- Home /
How to Copy a Component if it contains List?
I would like to copy a component using reflection, but my List fields never get copied they just reference to the first instance of the script.
T CopyComponent<T>(T original, GameObject destination) where T : Component
{
Type type = original.GetType();
var my_Component = destination.GetComponent<T>();
if (!my_Component)
{
my_Component = destination.AddComponent(type) as T;
}
FieldInfo[] fields = type.GetFields();
foreach (FieldInfo field in fields)
{
if (field.IsStatic)
{
continue;
}
field.SetValue(my_Component, field.GetValue(original));
}
PropertyInfo[] props = type.GetProperties();
foreach (PropertyInfo prop in props)
{
if (!prop.CanWrite)
{
continue;
}
prop.SetValue(my_Component, prop.GetValue(original, null), null);
}
return my_Component as T;
}
Comment
Best Answer
Answer by Menyus777 · Apr 02, 2018 at 04:39 PM
using System.Collections;
using System;
using System.Reflection;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Allows you to copy a component and all of its values
/// </summary>
public class ComponentCopier{
/// <summary>
/// Copies a Component to the given GameObject, also ignores Static and Const variables
/// </summary>
/// <typeparam name="T">The type of your Component</typeparam>
/// <param name="original">The instance of your Script</param>
/// <param name="destination">The GameObject you want your component to be copied to</param>
/// <returns>Returns a reference to your copied component</returns>
public static T CopyComponent<T>(T original, GameObject destination) where T : Component
{
//We ask for the Component type
Type type = original.GetType();
//Checks if the gameobject already has that comp, if error pops try this with non generic version -> "destination.GetComponent(type) as T;"
var my_Component = destination.GetComponent<T>();
//If my component is null we add a T component
if (!my_Component)
{
//Should not use Generic version here
my_Component = destination.AddComponent(type) as T;
}
//returns all the public fields of the T
FieldInfo[] fields = type.GetFields();
foreach (FieldInfo field in fields)
{
//If Static we do not copy, cos it would hurt the basic principle of static and pretty sure would be a runtime error too,
//in c# Const is also static so your Const values will be ignored too which is perfect for us
if (field.IsStatic)
{
continue;
}
//We ask for the type of the field this can be Int32, !!!Float which name is System.Single in C# not System.Float!!!, also this logic is extremely handful for you if you
//need to extend this script further
Type f_type = field.FieldType;
//is the type Generic? -> <T>
if (f_type.IsGenericType)
{
//So because .Net 3.5 aint supporting some nice features for Generic type the below code can be read,
//Please note the below lines can make coders vomit easily so proceed with caution
//Gets the Generic Type>
Type elementType = f_type.GetGenericArguments()[0];
//Is the code is a list
if (f_type.ToString() == String.Format("System.Collections.Generic.List`1[{0}]", elementType))
{
//Lets ask the type of a List<>
Type type_of_a_list = typeof(List<>);
//Lets ask the type of a List<> that contains our specific type like this cutie here: List<Vector3>
Type copy_List = type_of_a_list.MakeGenericType(elementType);
//so what Activator.CreateInstance does is that it makes an instance, that we can copy our List<T>, what we will get from the original component
IList instance = (IList)Activator.CreateInstance(copy_List);
//Our original List, dont ever try to cast it into object, otherwise runtime error cant cast source to target
IList instance_original = (IList)field.GetValue(original);
//Basic component copy
foreach(var element in instance_original)
{
instance.Add(element);
}
field.SetValue(my_Component, instance);
}
else
{
//Implement if you want it, i dont atm....
MonoBehaviour.print("Your code contains a generic Type but your component copier does not Copied it!");
}
}
else
{
field.SetValue(my_Component, field.GetValue(original));
}
}
//Below code detects properties, if you are not sure what is a property check it in MSD: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties
PropertyInfo[] props = type.GetProperties();
foreach (PropertyInfo prop in props)
{
//if my property only has a get function then i cant read it
if (!prop.CanWrite)
{
continue;
}
prop.SetValue(my_Component, prop.GetValue(original, null), null);
}
return my_Component as T;
}
}