SolutionsDesign / Algorithmia

Algorithm and data-structure library for .NET 4.5.2+/Netstandard 2.0+. Algorithmia contains sophisticated algorithms and data-structures like graphs, priority queues, command, undo-redo and more.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

MultiValueDictionary.Clone() do not clone inner HashSet value container

majerm opened this issue · comments

I mean it is a bug, because it is inner implementation structure. In my case it was a problem. I did not create pull-request, because fix is breaking change.
In my version it is fixed like this:

	public class MultiValueDictionary<...>

		private IEqualityComparer<TKey> _keyComparer;

                ....
		public MultiValueDictionary(IEqualityComparer<TKey> keyComparer, IEqualityComparer<TValue> valueComparer) : base(keyComparer)
		{
			_keyComparer = keyComparer;
			_valueComparer = valueComparer;
		}

		public MultiValueDictionary<TKey, TValue> Clone()
		{
			var clone = _keyComparer == null
				? new MultiValueDictionary<TKey, TValue>(_valueComparer)
				: new MultiValueDictionary<TKey, TValue>(_keyComparer, _valueComparer);
			foreach (var entry in this)
			{
				clone.Add(entry.Key, new HashSet<TValue>(entry.Value, _valueComparer));
			}
			return clone;
		}
                ....

The codebase doesn't contain a SortedMultiValueDictionary... ? :) The Clone() method currently there does a memberwise clone, which will shallowcopy the contents of all members? Not sure why you'd do it this way?

Sorry, its from my modification. It is in MultiValueDictionary.

My test with current version in nuget:

using SD.Tools.Algorithmia.GeneralDataStructures;
using System;
using System.Collections.Generic;

namespace TestApp
{
    internal static class Program
    {
        class ModifiedMultiValueDictionary<TKey, TValue>: MultiValueDictionary<TKey, TValue>
        {
            public ModifiedMultiValueDictionary<TKey, TValue> Clone2()
            {
                var clone = new ModifiedMultiValueDictionary<TKey, TValue>();
                foreach (var entry in this)
                {
                    clone.Add(entry.Key, new HashSet<TValue>(entry.Value));
                }
                return clone;
            }
        }

        [STAThread]
        private static void Main()
        {
            var original = new MultiValueDictionary<string, string>();
            original.AddRange("test1", new string[] { "A", "B" });
            original.AddRange("test2", new string[] { "C", "D" });
            original.AddRange("test3", new string[] { "E", "F" });
            
            var clone = original.Clone();
            clone.Remove("test1");
            clone.Remove("test2", "D");
            original.Remove("test3", "F");

            System.Diagnostics.Debug.WriteLine(@"MultiValueDictionary ORIGINAL");
            foreach (var item in original)
            {
                System.Diagnostics.Debug.WriteLine($"{item.Key}: {String.Join(", ", item.Value)}");
            }
            System.Diagnostics.Debug.WriteLine(@"MultiValueDictionary CLONE");
            foreach (var item in clone)
            {
                System.Diagnostics.Debug.WriteLine($"{item.Key}: {String.Join(", ", item.Value)}");
            }

            var original2 = new ModifiedMultiValueDictionary<string, string>();
            original2.AddRange("test1", new string[] { "A", "B" });
            original2.AddRange("test2", new string[] { "C", "D" });
            original2.AddRange("test3", new string[] { "E", "F" });
            
            var clone2 = original2.Clone2();
            clone2.Remove("test1");
            clone2.Remove("test2", "D");
            original2.Remove("test3", "F");

            System.Diagnostics.Debug.WriteLine(@"ModifiedMultiValueDictionary ORIGINAL");
            foreach (var item in original2)
            {
                System.Diagnostics.Debug.WriteLine($"{item.Key}: {String.Join(", ", item.Value)}");
            }
            System.Diagnostics.Debug.WriteLine(@"ModifiedMultiValueDictionary CLONE");
            foreach (var item in clone2)
            {
                System.Diagnostics.Debug.WriteLine($"{item.Key}: {String.Join(", ", item.Value)}");
            }
        }
}

Output:

MultiValueDictionary ORIGINAL
test2: C
test3: E
MultiValueDictionary CLONE
test2: C
test3: E
ModifiedMultiValueDictionary ORIGINAL
test1: A, B
test2: C, D
test3: E
ModifiedMultiValueDictionary CLONE
test2: C
test3: E, F

I was expecting the second behavior, but mabye I am wrong. Feel free to close issue.

Anyway, thank you very much for the great library :)

Hmm, to my knowledge the memberwise clone implemented in the original should give the same results for your modified dictionary too. But as it's a shallow clone, you might not want it for your purposes. (as it does a memberwise clone but not a deep copy)

As our code works as intended, I think I have to close this issue, sorry :)

Anyway, thank you very much for the great library :)

Thanks! :)