How to make extension unionWith for Hashset

I am trying to make an extension for the custom type. This is my code. I don't know how my source becomes zero in this code. Even in the debug part hashset temp is giving me a list of 10 logevents. But in the final the source is becoming zero.

public static void UnionSpecialWith(this HashSet<LogEvent> source, List<LogEvent> given,IEqualityComparer<LogEvent> comparer)
{
    List<LogEvent> original = new List<LogEvent>(source);
    List<LogEvent> second = given.Condense(comparer);

    source = new HashSet<LogEvent>(original.Condense(comparer),comparer);

    foreach (LogEvent logEvent in second)
    {
        if (original.Contains(logEvent, comparer))
        {
            int index = original.FindIndex(x => comparer.Equals(x, logEvent));
            original[index].filesAndLineNos.MergeFilesAndLineNos(logEvent.filesAndLineNos);
        }
        else
            original.Add(logEvent);
    }
#if DEBUG
    String content = String.Join(Environment.NewLine, original.Select(x => x.GetContentAsEventsOnly()));
    HashSet<LogEvent> temp = new HashSet<LogEvent>(original, comparer);
#endif
    source = new HashSet<LogEvent>(original, comparer);
}

Can anybody point me out what is wrong?

EDIT: This is my custom type. Whenever I found a duplicate , I want to merge it's "filesAndLineNos" with the original one. This is what I am trying to achieve with the above code.

public class LogEvent 
{
    public String mainEventOriginal;
    public String subEventOriginal;
    public String mainEvent;
    public String subEvent;
    public int level;
    public Dictionary<String,HashSet<int>> filesAndLineNos;
}

The usage is something like

HashSet<LogEvent> required = new HashSet<LogEvent>(initialUniqueSet);
required.UnionSpecialWith(givenListOfLogEvents);
Jon Skeet
people
quotationmark

This is simply a matter of parameters being passed by value in .NET by default. You're changing the value of source to refer to a different HashSet, and that doesn't change the caller's variable at all. Assuming that Condense doesn't modify the list (I'm unaware of that method) your method is as pointless as:

public void TrimString(string text)
{
    // This changes the value of the *parameter*, but doesn't affect the original
    // *object* (strings are immutable). The caller won't see any effect!
    text = text.Trim();
}

If you call the above with:

string foo = "   hello   ";
TrimString(foo);

... then foo is still going to refer to a string with contents " hello ". Obviously your method is more complicated, but the cause of the problem is the same.

Either your extension method needs to modify the contents of the original HashSet passed in via the source parameter, or it should return the new set. Returning the new set is more idiomatically LINQ-like, but HashSet.UnionWith does modify the original set - it depends which model you want to be closer to.

EDIT: If you want to modify the set in place, but effectively need to replace the contents entirely due to the logic, then you might want to consider creating the new set, then clearing the old and adding all the contents back in:

public static void UnionSpecialWith(this HashSet<LogEvent> source,
                                    List<LogEvent> given,
                                    IEqualityComparer<LogEvent> comparer)
{
    List<LogEvent> original = new List<LogEvent>(source);
    List<LogEvent> second = given.Condense(comparer);

    foreach (LogEvent logEvent in second)
    {
        if (original.Contains(logEvent, comparer))
        {
            int index = original.FindIndex(x => comparer.Equals(x, logEvent));
            original[index].filesAndLineNos
                           .MergeFilesAndLineNos(logEvent.filesAndLineNos);
        }
        else
        {
            original.Add(logEvent);
        }
    }
    source.Clear();
    foreach (var item in original)
    {
        source.Add(item); 
    }
}

However, note:

  • This does not replace the comparer in the existing set. You can't do that.
  • It's pretty inefficient in general. It feels like a Dictionary would be a better fit, to be honest.

people

See more on this question at Stackoverflow