private static void ScanForChanges(ContextPair contextPair, ObjectPath objectPath, ChangeCollection result) { var pathType = GetTypeFromObjects( objectPath.OldInstance, objectPath.NewInstance); if (IsSimpleType(pathType)) { var shallowChange = GetShallowChange( objectPath.BasePropertyPath, objectPath.OldInstance, objectPath.NewInstance); if (shallowChange != Change.Empty) { result.Add(shallowChange); if (objectPath.ContainerChange != Change.Empty) { result.Add(objectPath.ContainerChange); } } } else if (IsEnumerableType(pathType)) { Array itemsOldArray; Array itemsNewArray; if (pathType.IsArray) { itemsOldArray = (Array)objectPath.OldInstance; itemsNewArray = (Array)objectPath.NewInstance; } else { var genericType = pathType .GetInterfaces() .Single(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable <>)) .GetGenericArguments() .Single(); var toArrayMethod = typeof(Enumerable) .GetMethods() .Single(x => x.Name == nameof(Enumerable.ToArray) && x.IsGenericMethod) .MakeGenericMethod(genericType); var emptyMethod = typeof(Array) .GetMethods() .Single(x => x.Name == nameof(Array.Empty) && x.IsGenericMethod) .MakeGenericMethod(genericType); itemsOldArray = objectPath.OldInstance == null ? (Array)emptyMethod.Invoke(null, Array.Empty <object>()) : (Array)toArrayMethod.Invoke(null, new[] { objectPath.OldInstance }); itemsNewArray = objectPath.NewInstance == null ? (Array)emptyMethod.Invoke(null, Array.Empty <object>()) : (Array)toArrayMethod.Invoke(null, new[] { objectPath.NewInstance }); } var maxCount = Math.Max(itemsOldArray.Length, itemsNewArray.Length); var newItemsOldArray = new object[maxCount]; var newItemsNewArray = new object[maxCount]; Array.Copy(itemsOldArray, newItemsOldArray, itemsOldArray.Length); Array.Copy(itemsNewArray, newItemsNewArray, itemsNewArray.Length); var shallowChange = GetShallowChange( objectPath.BasePropertyPath ?? string.Empty, objectPath.OldInstance, objectPath.NewInstance); for (var i = 0; i < maxCount; i++) { var itemOldInstance = newItemsOldArray[i]; var itemNewInstance = newItemsNewArray[i]; var itemType = GetTypeFromObjects(itemOldInstance, itemNewInstance); if (itemType == null) { continue; } var arrayItemObjectPath = new ObjectPath() { BasePropertyPath = AddToPropertyPath( objectPath.BasePropertyPath, i.ToString()), OldInstance = itemOldInstance, NewInstance = itemNewInstance, Properties = itemType.GetProperties(), ContainerChange = shallowChange }; contextPair.ObjectPathQueue.Enqueue(arrayItemObjectPath); } } else { EnqueueObjectPathQueues( contextPair, objectPath); } }
private static void ScanForChanges(ContextPair contextPair, ObjectPath objectPath, ChangeCollection result) { var pathType = GetTypeFromObjects( objectPath.OldInstance, objectPath.NewInstance); if (IsSimpleType(pathType)) { var shallowChange = GetShallowChange( objectPath.BasePropertyPath, objectPath.OldInstance, objectPath.NewInstance); if (shallowChange == Change.Empty) { return; } result.Add(shallowChange); if (objectPath.ParentChanges == null) { return; } foreach (var parentChange in objectPath.ParentChanges) { result.Add(parentChange); } } else if (IsEnumerableType(pathType)) { if (pathType == null) { throw new InvalidOperationException("Type was enumerable despite being null."); } Array?itemsOldArray; Array?itemsNewArray; if (pathType.IsArray) { itemsOldArray = (Array?)objectPath.OldInstance; itemsNewArray = (Array?)objectPath.NewInstance; } else { var genericType = pathType .GetInterfaces() .Single(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable <>)) .GetGenericArguments() .Single(); var toArrayMethod = typeof(Enumerable) .GetMethods() .Single(x => x.Name == nameof(Enumerable.ToArray) && x.IsGenericMethod) .MakeGenericMethod(genericType); var emptyMethod = typeof(Array) .GetMethods() .Single(x => x.Name == nameof(Array.Empty) && x.IsGenericMethod) .MakeGenericMethod(genericType); itemsOldArray = objectPath.OldInstance == null ? (Array)emptyMethod.Invoke(null, Array.Empty <object>()) : (Array)toArrayMethod.Invoke(null, new[] { objectPath.OldInstance }); itemsNewArray = objectPath.NewInstance == null ? (Array)emptyMethod.Invoke(null, Array.Empty <object>()) : (Array)toArrayMethod.Invoke(null, new[] { objectPath.NewInstance }); } var maxCount = Math.Max( itemsOldArray?.Length ?? 0, itemsNewArray?.Length ?? 0); var newItemsOldArray = new object[maxCount]; var newItemsNewArray = new object[maxCount]; Array.Copy(itemsOldArray ?? Array.Empty <object>(), newItemsOldArray, itemsOldArray?.Length ?? 0); Array.Copy(itemsNewArray ?? Array.Empty <object>(), newItemsNewArray, itemsNewArray?.Length ?? 0); var shallowChange = new Change( objectPath.BasePropertyPath ?? string.Empty, objectPath.OldInstance, objectPath.NewInstance); var newParentChanges = new[] { shallowChange }; var parentChanges = objectPath.ParentChanges == null ? newParentChanges : objectPath .ParentChanges .Union(newParentChanges) .Distinct() .ToArray(); for (var i = 0; i < maxCount; i++) { var itemOldInstance = newItemsOldArray[i]; var itemNewInstance = newItemsNewArray[i]; var itemType = GetTypeFromObjects(itemOldInstance, itemNewInstance); if (itemType == null) { continue; } var arrayItemObjectPath = new ObjectPath() { BasePropertyPath = AddToPropertyPath( objectPath.BasePropertyPath, i.ToString()), OldInstance = itemOldInstance, NewInstance = itemNewInstance, Properties = itemType.GetProperties(), ParentChanges = parentChanges }; contextPair.ObjectPathQueue.Enqueue(arrayItemObjectPath); } } else { EnqueueObjectPathQueues( contextPair, objectPath); } }