Distinct values from List of objects - c#

I need your help. I am trying to get distinct values from List of objects.
My class looks like this:
class Chromosome
{
public bool[][] body { get; set; }
public double fitness { get; set; }
}
Now I have List<Chromosome> population. And now what I need is a way, how I can get new list: List<Chromosome> newGeneration. This new list will contain only unique chromosomes from original list - population.Chromosome is unique, when his whole body (which in this case is 2D bool array) is unique in comparison to the other chromosomes.
I know, that there is something like MoreLINQ, but I am not sure, whether I should use 3rd party code and I know that I should overwrite some methods, but I am kind of lost. So I would really appreciate some nice step by step description, that even idiot could accomplish :)
THX

First, implement the equality operator (this goes into class Chromosome):
public class Chromosome : IEquatable<Chromosome>
{
public bool[][] body { get; set; }
public double fitness { get; set; }
bool IEquatable<Chromosome>.Equals(Chromosome other)
{
// Compare fitness
if(fitness != other.fitness) return false;
// Make sure we don't get IndexOutOfBounds on one of them
if(body.Length != other.body.Length) return false;
for(var x = 0; x < body.Length; x++)
{
// IndexOutOfBounds on inner arrays
if(body[x].Length != other.body[x].Length) return false;
for(var y = 0; y < body[x].Length; y++)
// Compare bodies
if(body[x][y] != other.body[x][y]) return false;
}
// No difference found
return true;
}
// ReSharper's suggestion for equality members
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != this.GetType())
{
return false;
}
return this.Equals((Chromosome)obj);
}
public override int GetHashCode()
{
unchecked
{
return ((this.body != null ? this.body.GetHashCode() : 0) * 397) ^ this.fitness.GetHashCode();
}
}
}
Then, use Distinct:
var newGeneration = population.Distinct().ToList();

public class ChromosomeBodyComparer : IEqualityComparer<Chromosome>
{
private bool EqualValues(bool[][] left, bool[][] right)
{
if (left.Length != right.Length)
{
return false;
}
return left.Zip(right, (x, y) => x.SequenceEquals(y)).All();
}
public bool Equals(Chromosome left, Chromosome right)
{
return EqualValues(left.body, right.body)
}
//implementing GetHashCode is hard.
// here is a rubbish implementation.
public int GetHashCode(Chromosome c)
{
int numberOfBools = c.body.SelectMany(x => x).Count();
int numberOfTrues = c.body.SelectMany(x => x).Where(b => b).Count();
return (17 * numberOfBools) + (23 * numberOfTrues);
}
}
Called by:
List<Chromosome> nextGeneration = population
.Distinct(new ChromosomeBodyComparer())
.ToList();

Related

C# Dictionary that uses an Unordered Pair as its Key?

I'm trying to create a Dictionary is C# that takes an Unordered Pair of Indices as its Key.
For example:
exampleDictionary[new UnorderedPair(x,y)] and exampleDictionary[new UnorderedPair(y,x)] should both return the same value.
Is there a way to create a custom unordered collection other than using a HashSet? Or some way to create an unordered Tuple?
This question is similar to what I'm trying to accomplish, except in C# rather than python.
If the type is not your own or you can't or don't want to modify refer to Theodor Zoulias's answer
Otherwise, assuming that UnorderedPair is your own class you can modify what you could do is e.g.
[Serializable]
public class UnorderedPair<T> : IEquatable<UnorderedPair<T>>
{
public T X;
public T Y;
public UnorderedPair()
{
}
public UnorderedPair(T x, T y)
{
X = x;
Y = y;
}
public bool Equals(UnorderedPair<T> other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
// For equality simply include the swapped check
return X.Equals(other.X) && Y.Equals(other.Y) || X.Equals(other.Y) && Y.Equals(other.X);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != GetType())
{
return false;
}
return Equals((UnorderedPair<T>)obj);
}
public override int GetHashCode()
{
// and for the HashCode (used as key in HashSet and Dictionary) simply order them by size an hash them again ^^
var hashX = X == null ? 0 : X.GetHashCode();
var hashY = Y == null ? 0 : Y.GetHashCode();
return HashCode.Combine(Math.Min(hashX,hashY), Math.Max(hashX,hashY));
}
public static bool operator ==(UnorderedPair<T> left, UnorderedPair<T> right)
{
return Equals(left, right);
}
public static bool operator !=(UnorderedPair<T> left, UnorderedPair<T> right)
{
return !Equals(left, right);
}
}
and then e.g.
var testDict = new Dictionary<UnorderedPair<int>, string>();
testDict.Add(new UnorderedPair<int>(1,2), "Hello World!");
Console.WriteLine(testDict[new UnorderedPair<int>(2,1)]);
As per suggestion by Jodrell in the comments you could even make the types swappable - not sure this would be ever needed - but this way you could even have a pair of different types:
[Serializable]
public class UnorderedPair<TX, TY> : IEquatable<UnorderedPair<TX, TY>>
{
public TX X;
public TY Y;
public UnorderedPair()
{
}
public UnorderedPair(TX x, TY y)
{
X = x;
Y = y;
}
public UnorderedPair(TY y, TX x)
{
X = x;
Y = y;
}
public override int GetHashCode()
{
// and for the HashCode (used as key in HashSet and Dictionary) simply order them by size an hash them again ^^
var hashX = X == null ? 0 : X.GetHashCode();
var hashY = Y == null ? 0 : Y.GetHashCode();
var combine = HashCode.Combine(Math.Min(hashX, hashY), Math.Max(hashX, hashY));
return combine;
}
public bool Equals(UnorderedPair<TX, TY> other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
if (typeof(TX) != typeof(TY))
{
return EqualityComparer<TX>.Default.Equals(X, other.X) && EqualityComparer<TY>.Default.Equals(Y, other.Y);
}
return EqualityComparer<TX>.Default.Equals(X, other.X) && EqualityComparer<TY>.Default.Equals(Y, other.Y)
|| X.Equals(other.Y) && Y.Equals(other.X);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj switch
{
UnorderedPair<TX, TY> other => Equals(other),
UnorderedPair<TY, TX> otherSwapped => Equals(otherSwapped),
_ => false
};
}
public static bool operator ==(UnorderedPair<TX, TY> left, UnorderedPair<TX, TY> right)
{
return Equals(left, right);
}
public static bool operator !=(UnorderedPair<TX, TY> left, UnorderedPair<TX, TY> right)
{
return !Equals(left, right);
}
public static implicit operator UnorderedPair<TX, TY>(UnorderedPair<TY, TX> pair)
{
return new UnorderedPair<TX, TY>(pair.Y, pair.X);
}
}
and
var testDict = new Dictionary<UnorderedPair<int, double>, string>();
testDict.Add(new UnorderedPair<int, double>(1,2.5), "Hello World!");
Console.WriteLine(testDict[new UnorderedPair<double,int>(2.5,1)]);
(.NET Fiddle for both)
You could write a custom IEqualityComparer<UnorderedPair<T>> implementation, and pass it as argument to the constructor of your Dictionary<UnorderedPair<TKey>, TValue>. This way you won't have to modify your UnorderedPair<T> type, by overriding its Equals and GetHashCode methods. Below is an example of such a comparer for the ValueTuple<T1, T2> struct, with both T1 and T2 being the same type:
class UnorderedValueTupleEqualityComparer<T> : IEqualityComparer<(T, T)>
{
private readonly IEqualityComparer<T> _comparer;
public UnorderedValueTupleEqualityComparer(IEqualityComparer<T> comparer = default)
{
_comparer = comparer ?? EqualityComparer<T>.Default;
}
public bool Equals((T, T) x, (T, T) y)
{
if (_comparer.Equals(x.Item1, y.Item1)
&& _comparer.Equals(x.Item2, y.Item2)) return true;
if (_comparer.Equals(x.Item1, y.Item2)
&& _comparer.Equals(x.Item2, y.Item1)) return true;
return false;
}
public int GetHashCode((T, T) obj)
{
int h1 = _comparer.GetHashCode(obj.Item1);
int h2 = _comparer.GetHashCode(obj.Item2);
if (h1 > h2) (h1, h2) = (h2, h1);
return HashCode.Combine(h1, h2);
}
}
Usage example:
Dictionary<(int, int), string> dictionary = new(
new UnorderedValueTupleEqualityComparer<int>());
Inspired by #derHugo's answer and my comments on it,
Fiddle here
A generic implementation,
#nullable enable
public class UnorderedPair<T> : IEquatable<UnorderedPair<T>>
{
private static IEqualityComparer<T> comparer = EqualityComparer<T>.Default;
public T X { get; }
public T Y { get; }
public UnorderedPair(T x, T y)
{
X = x;
Y = y;
}
public bool Equals(UnorderedPair<T>? other)
{
if(other is null)
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
// For equality simply include the swapped check
return
comparer.Equals(X, other.X) && comparer.Equals(Y, other.Y)
||
comparer.Equals(X, other.Y) && comparer.Equals(Y, other.X);
}
public override bool Equals(object? obj)
{
return Equals(obj as UnorderedPair<T>);
}
public override int GetHashCode()
{
unchecked
{
return
(X is null ? 0 : comparer.GetHashCode(X))
+
(Y is null ? 0 : comparer.GetHashCode(Y));
}
}
public static bool operator ==(UnorderedPair<T>? left, UnorderedPair<T>? right)
{
return Equals(left, right);
}
public static bool operator !=(UnorderedPair<T>? left, UnorderedPair<T>? right)
{
return !Equals(left, right);
}
}
#nullable disable

Breakpoint in Equals implimentation on IEqualityComparer<CustomClass> is never hit

I have a simple custom class Point:
public class Point: IEqualityComparer<Point>
{
public double X;
public double Y;
public double Z;
private double[] startPointCoords;
public Point()
{
}
public Point(double[] pointArray)
{
this.startPointCoords = pointArray;
X = pointArray[0];
Y = pointArray[1];
Z = pointArray[2];
}
public bool Equals(Point x, Point y)
{
if(x.X == y.X && x.Y == y.Y && x.Z == y.Z)
{
return true;
}
return false;
}
public int GetHashCode(Point obj)
{
string xString = X.ToString().Replace(".", "");
string yString = Y.ToString().Replace(".", "");
string zString = Z.ToString().Replace(".", "");
int xInt = Convert.ToInt32(xString);
int yInt = Convert.ToInt32(yString);
int zInt = Convert.ToInt32(zString);
return xInt - yInt + zInt;
}
}
I am using this class in a Dictionary. I am checking for if the point instance has been added to the dictionary using:
if (!pointsToProcess.ContainsKey(startPoint))
{
pointsToProcess.Add(startPoint, startPoint);
}
I am debugging my code to make sure Equals is working correctly. My break point I have set in Point.Equals is never hit. I set a break point in Point.GetHashCode and it is never hit either. It seems like they are not being used.
I know that there are classes called Point in .Net. I am absolutely sure that all the Point that I have in my code is from my custom namespace.
Why would my Point.Equals and Point.GetHashCode not be reached when setting a break point?
The Equals(a, b) method is not hit by IEquatable, so you'll need to tailor it to suit the interface.
Try this one:
public class Point : IEquatable<Point>
{
public double X;
public double Y;
public double Z;
private double[] startPointCoords;
public Point()
{
}
public Point(double[] pointArray)
{
this.startPointCoords = pointArray;
X = pointArray[0];
Y = pointArray[1];
Z = pointArray[2];
}
public override bool Equals(object obj) => Equals(obj as Point);
public bool Equals(Point other)
{
if (other is null)
return false;
if (ReferenceEquals(this, other))
return true;
return this.X == other.X &&
this.Y == other.Y &&
this.Z == other.Z;
}
public override int GetHashCode()
{
string xString = X.ToString().Replace(".", "");
string yString = Y.ToString().Replace(".", "");
string zString = Z.ToString().Replace(".", "");
int xInt = Convert.ToInt32(xString);
int yInt = Convert.ToInt32(yString);
int zInt = Convert.ToInt32(zString);
return xInt - yInt + zInt;
}
}
Also there are a lot of ways to implement hashcodes in C# for custom objects. While not perfect, one simple way would be using anonymous object hashing:
public override int GetHashCode()
{
return new { X, Y, Z }.GetHashCode();
}

How to sort List<T> in c#

I've got a List<Card>, and I want to sort these cards
So, I'm looking for a method to sort them with different criterias, like their ID, their Name ...
public class Card : IComparer
{
public string ID;
public string Name;
public int CompareId(object firstCard, object secondCard)
{
Card c1 = (Card)firstCard;
Card c2 = (Card)secondCard;
return c1.Id.CompareTo(c2.Id);
}
}
But then, visual studio sent me an error :
'Card' does not implement interface member 'IComparer<Card>.Compare(Card, Card)'
You, probably, want to have your class Comparable not a Comparator
public class Card : IComparable<Card>
{
public string ID;
public string Name;
public int CompareTo(Card other)
{
if (null == other)
return 1;
// string.Compare is safe when Id is null
return string.Compare(this.Id, other.Id);
}
}
then
List<Card> myList = ...
myList.Sort();
Edit: If you want to have several criteria to choose from, you have to implement several Comparers as separated classes, e.g.
public sealed class CardByIdComparer : IComparer<Card>
{
public int Compare(Card x, Card y)
{
if (object.ReferenceEquals(x, y))
return 0;
else if (null == x)
return -1;
else if (null == y)
return 1;
else
return string.Compare(x.Id, y.Id);
}
}
and when sorting provide the required:
List<Card> myList = ...
myList.Sort(new CardByIdComparer());
Edit 2: (inspired by spender's library). If you want to combine several comparers into one (i.e. use comparer1, on tie - comparer2 etc.)
public sealed class ComparerCombined<T> : IComparer<T> {
private IComparer<T>[] m_Comparers;
public ComparerCombined(params IComparer<T>[] comparers) {
if (null == comparers)
throw new ArgumentNullException(nameof(comparers));
m_Comparers = comparers
.Select(item => item == null ? Comparer<T>.Default : item)
.Where(item => item != null)
.Distinct()
.ToArray();
}
public int Compare(T x, T y) {
if (object.ReferenceEquals(x, y))
return 0;
else if (null == x)
return -1;
else if (null == y)
return 1;
foreach (var comparer in m_Comparers) {
int result = comparer.Compare(x, y);
if (result != 0)
return result;
}
return 0;
}
}
usage:
myList.Sort(new ComparerCombined(
new CardByIdComparer(), // Sort By Id
new CardByNameComparer() // On tie (equal Id's) sort by name
));
The easiest way You can use Linq:
List<Card> objSortedList = objListObject.OrderBy(o=>o.ID).ToList();
or
List<Card> objSortedList = objListObject.OrderByDescending(o=>o.ID).ToList();
Good examples for demonstrate the concept of
List<T>.Sort(IComparer <T>) method check the link please.
IComparer<T> in this example compare method used for strings IComparer<T>
but you can use this for ID(int) too.
using System;
using System.Collections.Generic;
class GFG : IComparer<string>
{
public int Compare(string x, string y)
{
if (x == null || y == null)
{
return 0;
}
// "CompareTo()" method
return x.CompareTo(y);
}
}
public class geek
{
public static void Main()
{
List<string> list1 = new List<string>();
// list elements
list1.Add("C++");
list1.Add("Java");
list1.Add("C");
list1.Add("Python");
list1.Add("HTML");
list1.Add("CSS");
list1.Add("Scala");
list1.Add("Ruby");
list1.Add("Perl");
int range = 4;
GFG gg = new GFG();
Console.WriteLine("\nSort a range with comparer:");
// sort the list within a
// range of index 1 to 4
// where range = 4
list1.Sort(1, range, gg);
Console.WriteLine("\nBinarySearch and Insert Dart");
// Binary Search and storing
// index value to "index"
int index = list1.BinarySearch(0, range,
"Dart", gg);
if (index < 0)
{
list1.Insert(~index, "Dart");
range++;
}
}
}
You need to implement IComparer
public int Compare(Card card1, Card card2)
{
if (card1.ID > card2.ID)
return 1; //move card1 up
if (card2.ID < card1.ID)
return -1; //move card2 up
return 0; //do nothing
}

UnitTesting List<T> of custom objects with List<S> of custom objects for equality

I'm writing some UnitTests for a parser and I'm stuck at comparing two List<T> where T is a class of my own, that contains another List<S>.
My UnitTest compares two lists and fails. The code in the UnitTest looks like this:
CollectionAssert.AreEqual(list1, list2, "failed");
I've written a test scenario that should clarify my question:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ComparerTest
{
class Program
{
static void Main(string[] args)
{
List<SimplifiedClass> persons = new List<SimplifiedClass>()
{
new SimplifiedClass()
{
FooBar = "Foo1",
Persons = new List<Person>()
{
new Person(){ ValueA = "Hello", ValueB="Hello"},
new Person(){ ValueA = "Hello2", ValueB="Hello2"},
}
}
};
List<SimplifiedClass> otherPersons = new List<SimplifiedClass>()
{
new SimplifiedClass()
{
FooBar = "Foo1",
Persons = new List<Person>()
{
new Person(){ ValueA = "Hello2", ValueB="Hello2"},
new Person(){ ValueA = "Hello", ValueB="Hello"},
}
}
};
// The goal is to ignore the order of both lists and their sub-lists.. just check if both lists contain the exact items (in the same amount). Basically ignore the order
// This is how I try to compare in my UnitTest:
//CollectionAssert.AreEqual(persons, otherPersons, "failed");
}
}
public class SimplifiedClass
{
public String FooBar { get; set; }
public List<Person> Persons { get; set; }
public override bool Equals(object obj)
{
if (obj == null) { return false;}
PersonComparer personComparer = new PersonComparer();
SimplifiedClass obj2 = (SimplifiedClass)obj;
return this.FooBar == obj2.FooBar && Enumerable.SequenceEqual(this.Persons, obj2.Persons, personComparer); // I think here is my problem
}
public override int GetHashCode()
{
return this.FooBar.GetHashCode() * 117 + this.Persons.GetHashCode();
}
}
public class Person
{
public String ValueA { get; set; }
public String ValueB { get; set; }
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
Person obj2 = (Person)obj;
return this.ValueA == obj2.ValueA && this.ValueB == obj2.ValueB;
}
public override int GetHashCode()
{
if (!String.IsNullOrEmpty(this.ValueA))
{
//return this.ValueA.GetHashCode() ^ this.ValueB.GetHashCode();
return this.ValueA.GetHashCode() * 117 + this.ValueB.GetHashCode();
}
else
{
return this.ValueB.GetHashCode();
}
}
}
public class PersonComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
if (x != null)
{
return x.Equals(y);
}
else
{
return y == null;
}
}
public int GetHashCode(Person obj)
{
return obj.GetHashCode();
}
}
}
The question is strongly related to C# Compare Lists with custom object but ignore order, but I can't find the difference, other than I wrap a list into another object and use the UnitTest one level above.
I've tried to use an IEqualityComparer:
public class PersonComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
if (x != null)
{
return x.Equals(y);
}
else
{
return y == null;
}
}
public int GetHashCode(Person obj)
{
return obj.GetHashCode();
}
}
Afterwards I've tried to implement the ''IComparable'' interface thats allows the objects to be ordered. (Basically like this: https://stackoverflow.com/a/4188041/225808)
However, I don't think my object can be brought into a natural order. Therefore I consider this a hack, if I come up with random ways to sort my class.
public class Person : IComparable<Person>
public int CompareTo(Person other)
{
if (this.GetHashCode() > other.GetHashCode()) return -1;
if (this.GetHashCode() == other.GetHashCode()) return 0;
return 1;
}
I hope I've made no mistakes while simplifying my problem. I think the main problems are:
How can I allow my custom objects to be comparable and define the equality in SimplifiedClass, that relies on the comparision of subclasses (e.g. Person in a list, like List<Person>). I assume Enumerable.SequenceEqual should be replaced with something else, but I don't know with what.
Is CollectionAssert.AreEqual the correct method in my UnitTest?
Equals on a List<T> will only check reference equality between the lists themselves, it does not attempt to look at the items in the list. And as you said you don't want to use SequenceEqual because you don't care about the ordering. In that case you should use CollectionAssert.AreEquivalent, it acts just like Enumerable.SequenceEqual however it does not care about the order of the two collections.
For a more general method that can be used in code it will be a little more complicated, here is a re-implemented version of what Microsoft is doing in their assert method.
public static class Helpers
{
public static bool IsEquivalent(this ICollection source, ICollection target)
{
//These 4 checks are just "shortcuts" so we may be able to return early with a result
// without having to do all the work of comparing every member.
if (source == null != (target == null))
return false; //If one is null and one is not, return false immediately.
if (object.ReferenceEquals((object)source, (object)target) || source == null)
return true; //If both point to the same reference or both are null (We validated that both are true or both are false last if statement) return true;
if (source.Count != target.Count)
return false; //If the counts are different return false;
if (source.Count == 0)
return true; //If the count is 0 there is nothing to compare, return true. (We validated both counts are the same last if statement).
int nullCount1;
int nullCount2;
//Count up the duplicates we see of each element.
Dictionary<object, int> elementCounts1 = GetElementCounts(source, out nullCount1);
Dictionary<object, int> elementCounts2 = GetElementCounts(target, out nullCount2);
//It checks the total number of null items in the collection.
if (nullCount2 != nullCount1)
{
//The count of nulls was different, return false.
return false;
}
else
{
//Go through each key and check that the duplicate count is the same for
// both dictionaries.
foreach (object key in elementCounts1.Keys)
{
int sourceCount;
int targetCount;
elementCounts1.TryGetValue(key, out sourceCount);
elementCounts2.TryGetValue(key, out targetCount);
if (sourceCount != targetCount)
{
//Count of duplicates for a element where different, return false.
return false;
}
}
//All elements matched, return true.
return true;
}
}
//Builds the dictionary out of the collection, this may be re-writeable to a ".GroupBy(" but I did not take the time to do it.
private static Dictionary<object, int> GetElementCounts(ICollection collection, out int nullCount)
{
Dictionary<object, int> dictionary = new Dictionary<object, int>();
nullCount = 0;
foreach (object key in (IEnumerable)collection)
{
if (key == null)
{
++nullCount;
}
else
{
int num;
dictionary.TryGetValue(key, out num);
++num;
dictionary[key] = num;
}
}
return dictionary;
}
}
What it does is it makes a dictionary out of the two collections, counting the duplicates and storing it as the value. It then compares the two dictionaries to make sure that the duplicate count matches for both sides. This lets you know that {1, 2, 2, 3} and {1, 2, 3, 3} are not equal where Enumerable.Execpt would tell you that they where.

Problem with IDictionary<Complex Key, Complex Value>.Remove() implementation

Hi I don't understand why this code doesn't work - it don't remove key; I still get "2" on output.
Bencode.BencodeDict d = new myTorrent.Bencode.BencodeDict();
d.Dict.Add(new Bencode.BencodeString("info"), new Bencode.BencodeString("1"));
d.Dict.Add(new Bencode.BencodeString("info2"), new Bencode.BencodeString("2"));
d.Dict.Add(new Bencode.BencodeString("info3"), new Bencode.BencodeString("3"));
d.Remove(new Bencode.BencodeString("info2"));
Bencode.BencodeVariable s1;
s1 = d[new Bencode.BencodeString("info2")];
if (s1 != null)
Console.WriteLine(System.Text.UTF8Encoding.UTF8.GetString(s1.Encode()));
My BencodeDict and BencodeString
namespace myTorrent.Bencode
{
class BencodeDict : BencodeVariable, IDictionary<BencodeString, BencodeVariable>
{
private Dictionary<BencodeString, BencodeVariable> dict;
public BencodeDict() {
this.dict = new Dictionary<BencodeString,BencodeVariable>();
}
protected override void InternalDecode(BinaryReader data) { /*...*/ }
public override long ByteLength() { /*...*/ }
public override byte[] Encode() { /*...*/ }
//#region Overridden Methods
public override bool Equals(object ob)
{
if (ob == null)
return false;
BencodeDict y = ob as BencodeDict;
if (this.dict.Count != y.dict.Count)
return false;
BencodeVariable val;
foreach (KeyValuePair<BencodeString, BencodeVariable> keypair in this.dict)
{
if (!y.TryGetValue(keypair.Key, out val))
return false;
if (!keypair.Value.Equals(val))
return false;
}
return true;
}
public override int GetHashCode()
{
int result = 0;
foreach (KeyValuePair<BencodeString, BencodeVariable> keypair in this.dict)
{
result ^= keypair.Key.GetHashCode();
result ^= keypair.Value.GetHashCode();
}
return result;
}
#region IDictionary and IList methods
public void Add(BencodeString key, BencodeVariable value)
{
this.dict.Add(key, value);
}
public void Add(KeyValuePair<BencodeString, BencodeVariable> item)
{
this.dict.Add(item.Key, item.Value);
}
public void Clear()
{
this.dict.Clear();
}
public bool Contains(KeyValuePair<BencodeString, BencodeVariable> item)
{
if (!this.dict.ContainsKey(item.Key))
return false;
return this.dict[item.Key].Equals(item.Value);
}
public bool ContainsKey(BencodeString key)
{
foreach(KeyValuePair<BencodeString, BencodeVariable> pair in this.dict) {
if (pair.Key.Equals(key))
return true;
}
return false;
}
public void CopyTo(KeyValuePair<BencodeString, BencodeVariable>[] array, int arrayIndex) { /*...*/ }
public int Count
{
get { return this.dict.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(BencodeString key)
{
return this.dict.Remove(key);
}
public bool Remove(KeyValuePair<BencodeString, BencodeVariable> item)
{
return this.dict.Remove(item.Key);
}
public bool TryGetValue(BencodeString key, out BencodeVariable value)
{
foreach(KeyValuePair<BencodeString, BencodeVariable> pair in this.dict)
if ( pair.Key.Equals(key) ) {
value = pair.Value;
return true;
}
value = null;
return false;
}
public BencodeVariable this[BencodeString key]
{
get {
foreach(KeyValuePair<BencodeString, BencodeVariable> pair in this.dict)
if ( pair.Key.Equals(key) )
return pair.Value;
return null;
}
set { this.dict[key] = value; }
}
public ICollection<BencodeString> Keys
{
get { return this.dict.Keys; }
}
public ICollection<BencodeVariable> Values
{
get { return this.dict.Values; }
}
public IEnumerator<KeyValuePair<BencodeString, BencodeVariable>> GetEnumerator()
{
return this.dict.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.dict.GetEnumerator();
}
#endregion
}
}
class BencodeString : BencodeVariable
{
private byte[] str;
public BencodeString() {
this.str = null;
}
public BencodeString(string str) {
this.str = encoding.GetBytes(str);
}
public override bool Equals(object ob)
{
if (ob == null)
return false;
BencodeString y = ob as BencodeString;
return (encoding.GetString(this.str) == encoding.GetString(y.str));
}
public override int GetHashCode()
{
return this.str.GetHashCode();
}
}
You're relying on byte[].GetHashCode() doing something desirable. It won't. Arrays don't implement equality or hash operations - you'll get the default (identity) behaviour.
Rewrite your GetHashCode method as something like this:
public override int GetHashCode()
{
int result = 17;
foreach (byte b in str)
{
result = result * 31 + b;
}
return result;
}
(Also it's not clear what encoding is, but that's a different matter.)
Note that your Equals override will also throw a NullReferenceException if ob is a non-null reference, but not to a BencodeString.
EDIT: Assuming you're actually wanting to check for the byte arrays being the same, I wouldn't call Encoding.GetString in your equality check. There's no point. Just check the byte array contents directly. Something like this is a reasonable byte array equality check - although I'd generally prefer to write a generic equivalent:
private static bool ArraysEqual(byte[] x, byte[] y)
{
if (x == y)
{
return true;
}
if (x == null || y == null)
{
return false;
}
if (x.Length != y.Length)
{
return false;
}
for (int i = 0; i < x.Length; i++)
{
if (x[i] != y[i])
{
return false;
}
}
return true;
}
If you do want to check whether two byte arrays are decoded to equal strings, then you should use Encoding.GetString in both places... but that would rarely be an appropriate thing to do, IMO.
Mind you, it's not clear why you've got your own string-like class to start with. There are all kinds of potential problems here... unequal encodings, null references etc.
It is very important that values that are Equal also produce the same hash code. An obvious (but not necessarily efficient) workaround is this:
public override int GetHashCode()
{
return encoding.GetString(this.str).GetHashCode();
}
Making strings not behave as Unicode strings internally is a a code smell but possibly intentional here. It is normally applied at the outer interface. Your implementation would allow for the encoding to change after the string is read. But a really serious problem with that is that the dictionary is no longer valid when that happens. You won't be able to find keys back.

Categories

Resources