I would like to aggregate records stored in a List<>.
In the case I got a List<int>, the solution would be
var results = list.GroupBy(x => x).Select(g => g.Sum());
In the case I got a List<MyObject> with
public class MyObject
{
public MyObject(int pvalue)
{
Value = pvalue;
}
public int Value {get; set;}
public override bool Equals(object obj)
{
var p = obj as MyObject;
if (p == null)
return false;
return Value.Equals(p.Value);
}
public bool Equals(MyObject p)
{
if (p == null)
return false;
return Value.Equals(p.Value)
}
public override int GetHashCode()
{
int hash = 13;
hash = (hash * 7) + Value.GetHashCode();
return hash;
}
}
Then a solution would be :
myOjectList.GroupBy(x => x.Value).Select(g => new MyObject{ Value = g.Sum()});
Now in the case I got a List<IMyObject>, IMyObject being an interface (or an abstract class), and its concrete implementations got specific properties (example classes below), how can I solve the polymorphism in the Select of the previous Linq statement ?
public interface IMyObject
{
int Value {get; set;}
bool Equals(object obj);
int GetHashCode();
}
public class MyObject1 : IMyObject
{
public MyObject1(int pvalue, string pname)
{
Value = pvalue;
Name = pname;
}
public int Value {get; set;}
public string Name { get; set; }
public override bool Equals(object obj)
{
var p = obj as MyObject1;
if (p == null)
return false;
return Name.Equals(p.Name) && Value.Equals(p.Value);
}
public bool Equals(MyObject1 p)
{
if (p == null)
return false;
return Name.Equals(p.Name) && Value.Equals(p.Value)
}
public override int GetHashCode()
{
int hash = 13;
hash += (hash * 7) + Value.GetHashCode();
hash += (hash * 7) + Name.GetHashCode();
return hash;
}
}
public class MyObject2 : IMyObject
{
public MyObject2(int pvalue, int pvalue2)
{
Value = pvalue;
Value2 = pvalue2;
}
public int Value {get; set;}
public string Value2 { get; set; }
public override bool Equals(object obj)
{
var p = obj as MyObject1;
if (p == null)
return false;
return Value2.Equals(p.Value2) && Value.Equals(p.Value);
}
public bool Equals(MyObject1 p)
{
if (p == null)
return false;
return Value2.Equals(p.Value2) && Value.Equals(p.Value)
}
public override int GetHashCode()
{
int hash = 13;
hash += (hash * 7) + Value.GetHashCode();
hash += (hash * 7) + Value2.GetHashCode();
return hash;
}
}
Add a new operation to IMyObject to Clone the object:
public interface IMyObject {
int Value { get; set; }
bool Equals(object obj);
int GetHashCode();
IMyObject Clone();
}
Implement the Clone method:
public class MyObject1 : IMyObject {
...
public IMyObject Clone() {
return (IMyObject)this.MemberwiseClone();
}
}
public class MyObject2 : IMyObject {
...
public IMyObject Clone() {
return (IMyObject)this.MemberwiseClone();
}
}
Now you can use the Clone method to create an object to return (NOTE: you said nothing about how to pick the proper values for the other properties, so I arbitrarily used the first object in each group as the source).
var ans = myObjectList.GroupBy(x => x.Value).Select(g => { var rtnval = g.First().Clone(); rtnval.Value = g.Sum(m => m.Value); return rtnval; });
FIRST SOLUTION: REFLECTION
myListOfIMyObjects
.GroupBy(x => x.GetType())
.Select(x =>
{
var constr = x.Key.GetConstructor(Type.EmptyTypes);
var instance = (IMyObject)constr.Invoke(new object[0]);
instance.Value = x.Select(o => o.Value).Sum();
return instance;
})
.ToList();
Pro: you can embed all in a single Select, valid for all types implementing IMyObject, and you don't have to modify it if you add other classes like MyObject3 or MyObject4 later.
Con: Reflection is a fragile pattern, because you cannot rely on compilation checks. Also, all the MyObject classes must expose a parameterless constructor, and if it is not so, you will see an error only at runtime.
SECOND SOLUTION: OfType
var result1 = myListOfIMyObjects.OfType<MyObject1>();
var o1 = new MyObject1 { Value = result1.Sum(x => x.Value) };
var result2 = myListOfIMyObjects.OfType<MyObject2>();
var o2 = new MyObject2 { Value = result2.Sum(x => x.Value) };
var result = new List<IMyObject> { o1, o2 };
Pro: you have the compile-time checks, and the classes implementing IMyObject can have different constructors with different parameters.
Con: It's more verbose (you cannot embed that code in a single Select!), and if you add later other MyObject3, MyObject4 in your domain, you have to come back here and add other rows manually.
Related
There is a class of Products:
public class ProductWithFeatures
{
public string Name { get; set; }
public ICollection<Feature> Features { get; set; }
}
public class Feature
{
public int Id { get; set; }
public Feature(int Id)
{
this.Id = Id;
}
}
I want to write an IEqualityComparer for this (i already have one for Feature).
The one for Feature is like this:
public class FeatureComparer : IEqualityComparer<Feature>
{
public bool Equals(Feature x, Feature y)
{
return x.Id == y.Id;
}
public int GetHashCode(Feature obj)
{
return obj.Id;
}
}
And what i wrote so far on the other one is this:
public class ProductComparer : IEqualityComparer<LinqHomework.ProductWithFeatures>
{
public bool Equals(ProductWithFeatures x, ProductWithFeatures y)
{
return x.Name == y.Name && LinqHomework.FeatureComparer.Equals(x.Features, y.Features);
}
public int GetHashCode(ProductWithFeatures obj)
{
}
}
I can't find an answer anywhere about this. Does anybody know how to write it?
Two ProductWithFeaturess are equal if they have the same name, and have the same features in the same order.
public class ProductComparer : IEqualityComparer<LinqHomework.ProductWithFeatures>
{
public bool Equals(ProductWithFeatures x, ProductWithFeatures y)
{
return x.Name == y.Name && x.Features.SequenceEqual(y.Features, new LinqHomework.FeatureComparer());
}
public int GetHashCode(ProductWithFeatures obj)
{
int hash = obj.Name.GetHashCode();
var featureComparer = new LinqHomework.FeatureComparer();
foreach (var feature in obj.Features)
{
hash = hash * 23 + featureComparer.GetHashCode(feature);
}
return hash;
}
}
This is a simple approach, which can be improved in a number of ways.
First, let's give our FeatureComparer a Default property, so we don't need to keep creating new instances:
public class FeatureComparer : IEqualityComparer<Feature>
{
public static FeatureComparer Default { get; } = new FeatureComparer();
// ... as before
}
This lets us write:
public class ProductComparer : IEqualityComparer<LinqHomework.ProductWithFeatures>
{
public bool Equals(ProductWithFeatures x, ProductWithFeatures y)
{
return x.Name == y.Name && x.Features.SequenceEqual(y.Features, LinqHomework.FeatureComparer.Default);
}
public int GetHashCode(ProductWithFeatures obj)
{
int hash = obj.Name.GetHashCode();
foreach (var feature in obj.Features)
{
hash = hash * 23 + LinqHomework.FeatureComparer.Default.GetHashCode(feature);
}
return hash;
}
}
We're also not handling the case where our methods are passed null, or the name of a feature is null, so let's deal with those. We can also test whether x and y are the same object in Equals.
We'll also do the integer operations in an unchecked block in case it overflows (and the assembly is compiled with /checked).
Note that we use ReferenceEquals instead of ==, in case you end up implementing the == operator in your types.
public class ProductComparer : IEqualityComparer<LinqHomework.ProductWithFeatures>
{
public bool Equals(ProductWithFeatures x, ProductWithFeatures y)
{
if (ReferenceEquals(x, y))
return true;
if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
return false;
if (x.Name != y.Name)
return false;
if (ReferenceEquals(x.Features, y.Features))
return true;
if (ReferenceEquals(x.Features, null) || ReferenceEquals(y.Features, null))
return false;
if (!x.Features.SequenceEquals(y.Features, LinqHomework.FeatureComparer.Default))
return false;
return true;
}
public int GetHashCode(ProductWithFeatures obj)
{
if (ReferenceEquals(obj, null))
return 0;
unchecked
{
int hash = obj.Name?.GetHashCode() ?? 0;
if (!ReferenceEquals(obj.Features, null))
{
foreach (var feature in obj.Features)
{
hash = hash * 23 + LinqHomework.FeatureComparer.Default.GetHashCode(feature);
}
return hash;
}
}
}
}
It's really up to you. I personally would go for something like
public int GetHashCode( ProductWithFeatures obj )
{
string toHash = obj.Name;
foreach( var feature in obj.Features )
toHash += feature.GetHashCode();
return toHash.GetHashCode();
}
It's not the nicest code ever, but it does what it's supposed to do.
I have a function as below:
public var UpdateMapFetcher(int stationID, int typeID)
I need this function to return either string or int.
My return value is set as below
if (finaloutput == "System.String")
{
// param1[i] = Convert.ChangeType(typeID_New.ToString(), typeof(string));
returnvalue = returnvalue.ToString();
return returnvalue;
}
else if (finaloutput == "System.Int32")
{
int a=0;
a = Convert.ToInt32(returnvalue);
return a;
}
How to have either one data type as return value in dynamic environment.
My intuition tells me, that you are trying to convert string value to some type. In that case you can use:
public T UpdateMapFetcher<T>(int stationID)
{
//var someValue = "23";
return (T)Convert.ChangeType(someValue, typeof(T));
}
//then
var typed = UpdateMapFetcher<int>(6);
In case you don't know T, you can use mapping (0-int, 1-string, etc.):
public object UpdateMapFetcher(int stationID, int type)
{
var typeMap = new []{ typeof(int), typeof(string)};
//var someValue = "23";
return Convert.ChangeType(someValue, typeMap[type]);
}
//then
var untyped = UpdateMapFetcher(6, 0/*0 is int*/);
if (untyped.GetType() == typeof(int))
{ /*is int*/
}
Another solution is to use implicit conversions:
public class StringOrInt
{
private object value;
public ValueType Type { get; set; }
public static implicit operator StringOrInt(string value)
{
return new StringOrInt()
{
value = value,
Type = ValueType.String
};
}
public static implicit operator StringOrInt(int value)
{
return new StringOrInt()
{
value = value,
Type = ValueType.Int
};
}
public static implicit operator int(StringOrInt obj)
{
return (int)obj.value;
}
public static implicit operator string(StringOrInt obj)
{
return (string)obj.value;
}
}
public enum ValueType
{
String,
Int
}
And then (simplified):
public static StringOrInt UpdateMapFetcher(int stationID, int typeID)
{
if (typeID == 0)
return "Text";
return 23;
}
private static void Main(string[] args)
{
var result = UpdateMapFetcher(1, 1);
if (result.Type == ValueType.String) { }//can check before
int integer = result;//compiles, valid
string text = result;//compiles, fail at runtime, invalid cast
}
you can return an object. You'd have to subsequently check for types in your consuming method. I assume that won't be a problem in your usecase.
your method signature is therefore:
public object UpdateMapFetcher(int stationID, int typeID)
You also have the option of using the out keyword, which permits you to accept both into variables and check after the function has been called.
public void UpdateMapFetcher(int stationID, int typeID, out int intValue, out string strValue)
// or int return val and out string value
public int UpdateMapFetcher(int stationID, int typeID, out string strValue)
With the use appearing something like this:
int intVal;
string strVal;
UpdateMapFetcher(stationID, typeID, out intVal, out strVal);
if (strVal != null)
{
doSomethingWithString(strVal);
}
else
{
doSomethingWithInt(intVal);
}
Frankly, I would just return a Tuple, with string being non-null indicating string value to use, and null as indicator for int return
public Tuple<string, int> UpdateMapFetcher(int stationID, int typeID) {
if (finaloutput == "System.String")
{
// param1[i] = Convert.ChangeType(typeID_New.ToString(), typeof(string));
returnvalue = returnvalue.ToString();
return new Tuple<string, int>(returnvalue, 0);
}
else if (finaloutput == "System.Int32")
{
int a=0;
a = Convert.ToInt32(returnvalue);
return new Tuple<string, int>(null, a);
}
}
On consumer side
var rc = UpdateMapFetcher( .... );
if (rc.Item1 != null) {
// code to use string value
} else {
// code to use int value
}
I would choose to return an object of new class which might look like this:
class Result {
public string StringValue { get; }
public string Int32Value { get; }
public bool IsString { get; }
public bool IsInt32 { get; }
public Result(string value) {
StringValue = value;
IsString = true;
}
public Result(int value) {
Int32Value = value;
IsInt32 = true;
}
}
This way you can check which Type is it by using Isxxx property. You can also enhance this with validation in value geters. F. e., for string it might look like this:
public string StringValue {
get {
if (IsString)
return m_stringValue;
throw new InvalidOperationException("Value is not a string.");
}
}
You can't really do exactly that, but there are several ways to do more or less what you want. You'd probably be better off change the design a little though.
Two ideas:
Either change your code to use two different methods, and call each of them as needed instead.
..Or return an object, which you can cast however you like..
..Or, use a generic method with TypeDescriptor, like the following.
Note that we here convert the value to string first even if it was an int, since we can then use a common method ConvertFromString() to convert it to whatever type T was.
public T UpdateMapFetcher<T>(int stationID, int typeID) {
// To allow parsing to the generic type T:
var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T));
if(converter != null)
{
return (T)converter.ConvertFromString(returnvalue.ToString());
}
else
{
return default(T);
}
}
Usage:
var result = MyExtensions.UpdateMapFetcher<string>(1, 2);
or:
var result = MyExtensions.UpdateMapFetcher<int>(1, 2);
You can return Object and cast to type which you want.
public Object UpdateMapFetcher(int stationID, int typeID)
if (finaloutput == "System.String")
{
// param1[i] = Convert.ChangeType(typeID_New.ToString(), typeof(string));
returnvalue = returnvalue.ToString();
return returnvalue;
}
else if (finaloutput == "System.Int32")
{
int a=0;
a = Convert.ToInt32(returnvalue);
return a;
}
A type that can contain either one type or another is usually called (unsurprisingly) Either. It is a special case of a sum type, basically a discriminated union, tagged union, or disjoint union with exactly two cases (instead of an arbitrary number).
Unfortunately, there does not exist an implementation of an Either type in the standard libraries, but there are plenty of implementations to be found on Google, GitHub, and elsewhere … and porting one of the existing implementations from e.g. Haskell or Scala isn't that hard, either.
It looks a bit like this (forgive my code, I don't actually know C♯ that well):
using System;
abstract class Either<A, B>
{
public abstract bool IsLeft { get; }
public abstract bool IsRight { get; }
public abstract A Left { get; }
public abstract B Right { get; }
public abstract A LeftOrDefault { get; }
public abstract B RightOrDefault { get; }
public abstract void ForEach(Action<A> action);
public abstract void ForEach(Action<B> action);
public abstract void ForEach(Action<A> leftAction, Action<B> rightAction);
private sealed class L : Either<A, B>
{
private A Value { get; }
public override bool IsLeft => true;
public override bool IsRight => false;
public override A Left => Value;
public override B Right { get { throw new InvalidOperationException(); } }
public override A LeftOrDefault => Value;
public override B RightOrDefault => default(B);
public override void ForEach(Action<A> action) => action(Value);
public override void ForEach(Action<B> action) {}
public override void ForEach(Action<A> leftAction, Action<B> rightAction) => leftAction(Value);
internal L(A value) { Value = value; }
}
private sealed class R : Either<A, B>
{
private B Value { get; }
public override bool IsLeft => false;
public override bool IsRight => true;
public override A Left { get { throw new InvalidOperationException(); } }
public override B Right => Value;
public override A LeftOrDefault => default(A);
public override B RightOrDefault => Value;
public override void ForEach(Action<A> action) {}
public override void ForEach(Action<B> action) => action(Value);
public override void ForEach(Action<A> leftAction, Action<B> rightAction) => rightAction(Value);
internal R(B value) { Value = value; }
}
public static Either<A, B> MakeLeft(A value) => new L(value);
public static Either<A, B> MakeRight(B value) => new R(value);
}
And you'd use it like this:
static class Program
{
public static void Main()
{
var input = Console.ReadLine();
int intResult;
var result = int.TryParse(input, out intResult) ? Either<int, string>.MakeLeft(intResult) : Either<int, string>.MakeRight(input);
result.ForEach(r => Console.WriteLine("You passed me the integer one less than " + ++r), r => Console.WriteLine(r));
}
}
I have a collection of objects where I want to find distinct values based on several properties.
I could do this:
var distinct = myValues.GroupBy(p => new { A = p.P1, B = p.P2 });
But I want to encapsulate the equality sementics. Something like this:
public interface IKey<T>
{
bool KeyEquals(T other);
}
public class MyClass : IKey<MyClass>
{
public string P1 { get; set; }
public string P2 { get; set; }
public bool KeyEquals(MyClass other)
{
if(object.ReferenceEquals(this, other)
return true;
if(other == null)
return false;
return this.P1 == other.P1 && this.P2 == other.P2;
}
}
Is there an O(N) way to get distinct values using my KeyEquals function?
If you can't change MyClass, you can implement an IEqualityComparer:
class MyClassComparer : IEqualityComparer<MyClass>
{
public bool Equals(MyClass m1, MyClass m2)
{
return m1.KeyEquals(m2);
}
public int GetHashCode(MyClass m)
{
return (m.P1.GetHashCode() *23 ) + (m.P2.GetHashCode() * 17);
}
}
And pass it to GroupBy
var distinct = myValues.GroupBy(p => p, new MyClassComparer());
I'm using xUnit and it doesn't have a way to determine if 2 IEnumerable<T> are equal if T is custom type.
I've tried using LINQ SequenceEqual but again as the instances of T are different this returns false;
Here is a basic test with a non-working IEqualityComparer
[Fact]
public void FactMethodName()
{
var one = new[] { new KeywordSchedule() { Id = 1 } };
var two = new[] { new KeywordSchedule() { Id = 1 } };
Assert.Equal(one, two, new KeywordScheduleComparer());
}
public class KeywordScheduleComparer : IEqualityComparer<IEnumerable<KeywordSchedule>>
{
public bool Equals(IEnumerable<KeywordSchedule> x, IEnumerable<KeywordSchedule> y)
{
return Object.ReferenceEquals(x, y) || (x != null && y != null && x.SequenceEqual(y));
}
public int GetHashCode(IEnumerable<KeywordSchedule> obj)
{
if (obj == null)
return 0;
return unchecked(obj.Select(e => e.GetHashCode()).Aggregate(0, (a, b) => a + b)); // BAD
}
}
I'm using this in an integration test, so I insert data from a IEnumerable into a DB at the start, then call my SUT to retrieve data from DB and compare.
If you can help me get a collection comparison working I'd appreciate it!
I just verified that this works fine with xUnit.net 1.9.2:
public class MyClass
{
public int ID { get; set; }
public string Name { get; set; }
}
public class MyClassComparer : IEqualityComparer<MyClass>
{
public bool Equals(MyClass x, MyClass y)
{
return x.ID == y.ID;
}
public int GetHashCode(MyClass obj)
{
return obj.ID.GetHashCode();
}
}
public class ExampleTest
{
[Fact]
public void TestForEquality()
{
var obj1 = new MyClass { ID = 42, Name = "Brad" };
var obj2 = new MyClass { ID = 42, Name = "Joe" };
Assert.Equal(new[] { obj1 }, new[] { obj2 }, new MyClassComparer());
}
}
So I'm not 100% clear why you need the extra comparer. Just the single comparer should be sufficient.
Well, your implementation is pending. You implemented custom comparer for IEnumerable<KeywordSchedule> but forgot to implement the same for KeywordSchedule.
x.SequenceEqual Still uses Comparer<T>.Default so it goes for reference comaprison and hence result is false.
public class KScheduleComparer : IEqualityComparer<KeywordSchedule>
{
public bool Equals(KeywordSchedule x, KeywordSchedule y)
{
return x.Id == y.Id;
}
public int GetHashCode(KeywordSchedule obj)
{
return obj.GetHashCode();
}
}
Then modify your Equals method in KeywordScheduleComparer class as below
public class KeywordScheduleComparer : IEqualityComparer<IEnumerable<KeywordSchedule>>
{
public bool Equals(IEnumerable<KeywordSchedule> x, IEnumerable<KeywordSchedule> y)
{
return Object.ReferenceEquals(x, y) || (x != null && y != null && x.SequenceEqual(y, new KScheduleComparer()));
}
public int GetHashCode(IEnumerable<KeywordSchedule> obj)
{
if (obj == null)
return 0;
return unchecked(obj.Select(e => e.GetHashCode()).Aggregate(0, (a, b) => a + b)); // BAD
}
}
You could do this more elegantly using FluentAssertions library. It has plenty assertion methods for collections.
public class MyClass
{
public int ID { get; set; }
public string Name { get; set; }
protected bool Equals(MyClass other)
{
return ID == other.ID;
}
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((MyClass) obj);
}
public override int GetHashCode()
{
unchecked
{
return (ID*397) ^ (Name != null ? Name.GetHashCode() : 0);
}
}
}
public class ExampleTest
{
[Fact]
public void TestForEquality()
{
var obj1 = new MyClass { ID = 42, Name = "Rock" };
var obj2 = new MyClass { ID = 42, Name = "Paper" };
var obj3 = new MyClass { ID = 42, Name = "Scissors" };
var obj4 = new MyClass { ID = 42, Name = "Lizard" };
var list1 = new List<MyClass> {obj1, obj2};
list1.Should().BeEquivalentTo(obj3, obj4);
}
}
my class:
public class myClass
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
public int D { get; set; }
}
and main example:
Dictionary<myClass, List<string>> dict = new Dictionary<myClass, List<string>>();
myClass first = new myClass();
first.A = 2;
first.B = 3;
myClass second = new myClass();
second.A = 2;
second.B = 3;
second.C = 5;
second.D = 6;
dict.Add(first, new List<string>());
if (dict.ContainsKey(second))
{
//
//should come here and update List<string> for first (and only in this example) key
//
}
else
{
//
//if myFirst object has difference vlues of A or B properties
//
dict.Add(second, new List<string>());
}
How to do this?
If you always want the dictionary only to compare on A and B, you have two options. Either use the constructor that implements IEqualityComparer<TKey> and put your comparison logic there, or have your class implement IEquateable<T> GetHashCode and Equals so the default comparer will give you the results you are looking for.
If you only want to compare on A and B in your one situation you will need to use the .Keys property and the Linq extension method Contains that allows you to pass in a IEqualityComparer<T>. However, when doing it this way you loose the speed benefits of using a Dictionary, so use it sparingly.
public class MyClassSpecialComparer : IEqualityComparer<myClass>
{
public bool Equals (myClass x, myClass y)
{
return x.A == y.A && x.B == y.B
}
public int GetHashCode(myClass x)
{
return x.A.GetHashCode() + x.B.GetHashCode();
}
}
//Special case for when you only want it to compare this one time
//NOTE: This will be much slower than a normal lookup.
var myClassSpecialComparer = new MyClassSpecialComparer();
Dictionary<myClass, List<string>> dict = new Dictionary<myClass, List<string>>();
//(Snip)
if (dict.Keys.Contains(second, myClassSpecialComparer ))
{
//
//should come here and update List<string> for first (and only in this example) key
//
}
//If you want it to always compare
Dictionary<myClass, List<string>> dict = new Dictionary<myClass, List<string>>(new MyClassSpecialComparer());
By default, comparison puts objects into buckets based on their hash code. A detailed comparison is then performed (by calling Equals) if two hash codes are the same. If your class neither provides GetHashCode or implements equality, the default object.GetHashCode will be used--in which case nothing specific to your class will be used for value comparison semantics. Only the same reference will be found. If you don't want this, implement GetHashCode and implement equality.
For example:
public class myClass
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
public int D { get; set; }
public bool Equals(myClass other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return other.A == A && other.B == B && other.C == C && other.D == D;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof (myClass)) return false;
return Equals((myClass) obj);
}
public override int GetHashCode()
{
unchecked
{
int result = A;
result = (result*397) ^ B;
result = (result*397) ^ C;
result = (result*397) ^ D;
return result;
}
}
}
Override in your myClass:
GetHashCode method
Equals method
To implement GetHashCode method you can just XOR GetHashCodes from your integer properties.
Optionally override ToString method and implement IEquatable interface