Copy one Generic List to another one

Weird question. I would like to copy a generic list from script A to another generic list from script B, but I would like to do this in another script “C”? Any thoughts? I cant seem to get anything working in Script C.

Both generic list have the same variable inside the public class, and variable range from strings, ints, floats, gameobjects, and transforms. I am thinking of doing it manually for each variable inside the generic lists, but I am trying to see if there is a faster way of doing it?

Thanks: James

If you want to copy a list from one script to another there are generally 3 different ways.

First of all if you are happy that both scripts will actually use the same list, you can simply assign the list of script1 to the list varable of script2. That way both scripts will be using the same List instance. So if script1 adds an element to the list, script2 will see that element as well.

The other two ways are about actually copying the content of the list. The first usual way is to simply create a new list and provide the source list from the other script as parameter to the constructor. This will copy the content of the source list into the newly created list.

If you want to do this copying more frequence and you want to avoid creating a new list all the time (to prevent garbage generation), the general approach is to first call Clear() on the target list to make it empty and then use AddRange and provide the source list.

To follow the code example of Cherno, here are the 3 solutions:

// both scripts will use the list of scriptA after that line
scriptB.list = scriptA.list;

// we just create a new list and fill it with the same content of the other one
scriptB.list = new List<YourElementType>(scriptA.list);

// This is the garbage friendly version
scriptB.list.Clear();
scriptB.list.AddRange(scriptA.list);

If you just want to have both scripts referencing the same list, you can simple use

scriptB.list = scriptA.list;

However, if you want to actually copy the list, I’d use his handy helper class I found online:

using System;
using System.Reflection;//for copying objects
using System.Linq;//for string[].Contains()
using UnityEngine;//for print

public class CopyData {
	
	/// <summary>
	/// Copies the data of one object to another. The target object gets properties of the first. 
	/// Any matching properties (by name) are written to the target.
	/// </summary>
	/// <param name="source">The source object to copy from</param>
	/// <param name="target">The target object to copy to</param>
	public static void CopyObjectData(object source, object target)
	{
		CopyObjectData(source, target, String.Empty, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
	}
	
	/// <summary>
	/// Copies the data of one object to another. The target object gets properties of the first. 
	/// Any matching properties (by name) are written to the target.
	/// </summary>
	/// <param name="source">The source object to copy from</param>
	/// <param name="target">The target object to copy to</param>
	/// <param name="excludedProperties">A comma delimited list of properties that should not be copied</param>
	/// <param name="memberAccess">Reflection binding access</param>
	public static void CopyObjectData(object source, object target, string excludedProperties, BindingFlags memberAccess)
	{
		string[] excluded = null;
		if (!string.IsNullOrEmpty(excludedProperties))
		{
			excluded = excludedProperties.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
		}
		
		MemberInfo[] miT = target.GetType().GetMembers(memberAccess);

		foreach (MemberInfo Field in miT)
		{
			string name = Field.Name;
			
			// Skip over excluded properties
			if (string.IsNullOrEmpty(excludedProperties) == false
			    && excluded.Contains(name))
			{
				continue;
			}
			
			
			if (Field.MemberType == MemberTypes.Field)
			{
				FieldInfo sourcefield = source.GetType().GetField(name);
				if (sourcefield == null) { continue; }
				
				object SourceValue = sourcefield.GetValue(source);
				((FieldInfo)Field).SetValue(target, SourceValue);
			}

			else if (Field.MemberType == MemberTypes.Property)
			{
				PropertyInfo piTarget = Field as PropertyInfo;
				PropertyInfo sourceField = source.GetType().GetProperty(name, memberAccess);
				if (sourceField == null) { continue; }
				
				if (piTarget.CanWrite && sourceField.CanRead)
				{
					object targetValue = piTarget.GetValue(target, null);
					object sourceValue = sourceField.GetValue(source, null);
					
					if (sourceValue == null) { continue; }
					
					if (sourceField.PropertyType.IsArray
					    && piTarget.PropertyType.IsArray
					    && sourceValue != null ) 
					{
						CopyArray(source, target, memberAccess, piTarget, sourceField, sourceValue);
					}
					else
					{
						CopySingleData(source, target, memberAccess, piTarget, sourceField, targetValue, sourceValue);
					}
				}
			}

		}
	}
	
	private static void CopySingleData(object source, object target, BindingFlags memberAccess, PropertyInfo piTarget, PropertyInfo sourceField, object targetValue, object sourceValue)
	{
		//instantiate target if needed
		if (targetValue == null
		    && piTarget.PropertyType.IsValueType == false
		    && piTarget.PropertyType != typeof(string))
		{
			if (piTarget.PropertyType.IsArray)
			{
				targetValue = Activator.CreateInstance(piTarget.PropertyType.GetElementType());
			}
			else
			{
				targetValue = Activator.CreateInstance(piTarget.PropertyType);
			}
		}
		
		if (piTarget.PropertyType.IsValueType == false
		    && piTarget.PropertyType != typeof(string))
		{
			CopyObjectData(sourceValue, targetValue, "", memberAccess);
			piTarget.SetValue(target, targetValue, null);
		}
		else
		{
			if (piTarget.PropertyType.FullName == sourceField.PropertyType.FullName)
			{
				object tempSourceValue = sourceField.GetValue(source, null);
				piTarget.SetValue(target, tempSourceValue, null);
			}
			else
			{
				CopyObjectData(piTarget, target, "", memberAccess);
			}
		}
	}
	
	private static void CopyArray(object source, object target, BindingFlags memberAccess, PropertyInfo piTarget, PropertyInfo sourceField, object sourceValue)
	{
		int sourceLength = (int)sourceValue.GetType().InvokeMember("Length", BindingFlags.GetProperty, null, sourceValue, null);
		Array targetArray = Array.CreateInstance(piTarget.PropertyType.GetElementType(), sourceLength);
		Array array = (Array)sourceField.GetValue(source, null);
		
		for (int i = 0; i < array.Length; i++)
		{
			object o = array.GetValue(i);
			object tempTarget = Activator.CreateInstance(piTarget.PropertyType.GetElementType());
			CopyObjectData(o, tempTarget, "", memberAccess);
			targetArray.SetValue(tempTarget, i);
		}
		piTarget.SetValue(target, targetArray, null);
	}
}

It can be used for virtually anything that needs to be coöied (as opposed to jsut a reference). It doesn’t matter if it’s a single variable, collection, or a whole script/class instance.

For you case, you would then use

CopyData.CopyObjectData(scriptA.list, scriptB.list);