programming in c# equality
DESCRIPTION
Programming in C# Equality. CSE / ECE 668 Prof . Roger Crawfis. Equality. What does it mean for to variables to be equal: Numeric types – easy Strings – some caveats Employee records - ?. Equality. Two basic types of equality: Value equality - PowerPoint PPT PresentationTRANSCRIPT
![Page 1: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/1.jpg)
Programming in C#Equality
CSE / ECE 668Prof. Roger Crawfis
![Page 2: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/2.jpg)
Equality
What does it mean for to variables to be equal:Numeric types – easyStrings – some caveatsEmployee records - ?
![Page 3: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/3.jpg)
Equality
Two basic types of equality: Value equality
Two variables are equal if they have the same value (mean the same thing).
Referential equality Two variables are equal if they refer to the same
instance (pointer or storage equality).Difference:
Are two 2008 Lamborghini Gallardo’s equal? Theoretically, it depends on the context.
![Page 4: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/4.jpg)
Default Equality
As expected, all value types have value-based equality.double a = 1.2;double b = 1.2;bool areEqual = a == b; // true.
Strings also have value equality.string name = “Crawfis”;string instructor = “Crawfis”;bool areEqual = a == b; // true.
![Page 5: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/5.jpg)
Default Equality
What is the default equality for classes?In C++ there is none.In C# there is. It is referential equality.
class foo {…};foo A = new foo();foo B = new foo();bool areEqual = A == B; // false.object C = A; // C refers to the same instance as A.bool areEqual = C == A; // true.
![Page 6: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/6.jpg)
Default Equality
Subtleties:int[] a = {0,1,2,3};int[] b = {0,1,2,3};
int[] a = {0,1,2,3};int[] b = a;
int[] a = null;int[] b = null;
a == b => false
a == b => true
a == b => true!
![Page 7: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/7.jpg)
Default Equality
The default equality for structs is simply the pairwise comparison between each of its fields.
That is, two structs are equal if all of their fields are equal.Value-based field are compared by value.Reference fields are compared by reference
(by default).
![Page 8: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/8.jpg)
Testing for Equality
There are actually five protocols or methods that you can use to test for equality.
They may provide different results!!!Witness one says they are the same.Witness two says they are different.
![Page 9: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/9.jpg)
Testing for Equality
Operators for equality (== and !=).Recall that all operators are defined as
static methods for a type. If there are different types on each side of
the conditional, the compiler decides which type to use.
All types have these operators defined.Unlike C++.
![Page 10: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/10.jpg)
Testing for Equality
The Object.Equals method exists for all types.
It is virtual and hence can be overridden.Determines at run-time which type’s
Equals method is called (Polymorphic).int x = 5;object y = 5;
x == y => compile time errorx.Equals(y) => truey.Equals(x) => true
Note: x is boxed!
![Page 11: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/11.jpg)
Testing for Equality
Why have both of these? If a variable is null, calling Equals on it will
result in a run-time exception.There is overhead associated with virtual
function calls (and all function calls).The == operator can be statically inlined.Sometimes we want different behavior:
We both own the same car (equals) but mine is not yours (not equals).
![Page 12: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/12.jpg)
Testing for Equality
There are also two static methods in the object class.static bool object.Equals(object o1, object o2); Simply calls o1.Equals if o1 != null.
static bool object.ReferenceEquals(object o1, object o2);
Since Equals can be overridden, this provides a forced reference equality check.
Note: object.ReferenceEquals(5,5) = false!
![Page 13: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/13.jpg)
The IEquatable<T> interface
Although the System.Object.Equals method can be applied to any type it will force a boxing of value types.
The IEquatable<T> interface allows value types which implement the interface to be called using a.Equals(b) without the boxing.
Used as a generic constraint on user classes:class Test<T> where T : IEquatable<T>
![Page 14: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/14.jpg)
Overriding Equality
In general, do not change the default behavior (semantics).
Implementing the default behavior for structs and the IComparable<T> interface can avoid boxing and provide good performance improvements.
Some immutable types may want different semantics: string, DateTime
![Page 15: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/15.jpg)
Overriding Equality
But what if I have Employee records and I want to check if two instances are equal?
Do not build this into the Employee class.Use plug-in comparison classes to
dynamically specify the behavior you want.This is what is used in the collection classes.
![Page 16: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/16.jpg)
Equality Semantics
If you override the semantics, then you must also override the hash code algorithm.
There are several rules that you should make sure you follow for each of ==, Equals, and GetHashCode.
If you override one of these you should probably override them all.
![Page 17: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/17.jpg)
Overriding GetHashCode
Rules for GetHashCode:Consistency – it must return the same value if
called repeated on the same object.Even if you change the object!!!!Base it on an immutable value of the object.
Equality – it must return the same value on two objects for which Equals returns true.
Robust – it should not throw exceptions.Efficient – GetHashCode should generate a
random distribution among all inputs.
![Page 18: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/18.jpg)
Overriding GetHashCode
For reference types, each instance has a unique hash-code based on the storage location.
Can add it to a Collection (Dictionary), change the contents, and still get it back out using the hash code.
If you override the hash code based on content rather than storage location, then the instance may need to be removed from the Dictionary, changed and added back.
![Page 19: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/19.jpg)
Overriding GetHashCode
Structs and value types are different, since they are never changed in-place, they are copied out, modified and copied back in.
Hence this problem exists regardless of whether you override GetHashCode.
struct X { public int x; }Dictionary<X, int> test = new Dictionary<X, int>();X t1 = new X();test[t1] = 5;t1.x = 3;test[t1] = 4;
Adds a new entry to the dictionary
![Page 20: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/20.jpg)
Overriding Equals
Rules for overriding equals:An object should equal itself (reflexive).An object can not equal null
Since it is an instance method call.Unless it is a Nullable type.
Equality is commutative and transitive.a == b => b == a; b == c => a ==c;
Equality operations are repeatableEquality is robust – no exceptions.
![Page 21: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/21.jpg)
Overriding == and !=
You should always override == for value types for efficiency.
You should never (or rarely) override it for reference types.
Rules for overriding ==a != b should be equal to !(a == b). It should have the same semantics as Equals.
Hence the previous rules apply.
![Page 22: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/22.jpg)
Example - Craps
namespace OSU.Gambling.Craps{ struct DiceRoll : IEquatable<DiceRoll> { internal DiceRoll(int die1, int die2) { this.die1 = die1; this.die2 = die2; } private int die1, die2;
![Page 23: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/23.jpg)
Example - Craps
namespace OSU.Gambling.Craps{ struct DiceRoll : IEquatable<DiceRoll> { internal DiceRoll(int die1, int die2) { this.die1 = die1; this.die2 = die2; } private int die1, die2;
I’ve never played this
game.
Requires two dice.
I really want to control the
creation, so make this constructor
internal or private.
Alas structs always have a public default constructor
![Page 24: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/24.jpg)
Example - Craps
public int Die1 { get { return die1+1; } } public int Die2 { get { return die2+1; } }
Add one to allow for a default value of zero.
Error checking should be done to ensure that die1 and die2 lie between 0 and 5.
![Page 25: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/25.jpg)
Example - Craps
public bool Equals(DiceRoll other) { return die1 == other.die1 && die2 == other.die2 || die1 == other.die2 && die2 == other.die1; } public override bool Equals(object other) { if (!(other is DiceRoll)) return false; return Equals((DiceRoll)other); // unbox the struct and compare. }
![Page 26: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/26.jpg)
Example - Craps
public static bool operator ==(DiceRoll die1, DiceRoll die2) { return die1.Equals(die2); } public static bool operator !=(DiceRoll die1, DiceRoll die2) { return !die1.Equals(die2); }
![Page 27: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/27.jpg)
Example - Craps
public override int GetHashCode() { return die1*11 + die2; }
public override int GetHashCode() { if (die1 > die2) return 11 * die1 + die2; else return 11 * die2 + die1; }
![Page 28: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/28.jpg)
Example - Craps
public override int GetHashCode() { return die1*11 + die2; }
public override int GetHashCode() { if (die1 > die2) return 11 * die1 + die2; else return 11 * die2 + die1; }
![Page 29: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/29.jpg)
Example - Craps
public static DiceRoll RollDice() { DiceRoll roll = new DiceRoll(); roll.die1 = random.Next(0,5); roll.die2 = random.Next(0,5); return roll; } private static readonly Random random; static DiceRoll() { random = new Random(System.DateTime.Now.Millisecond); } }
![Page 30: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/30.jpg)
Design Principle
The previous code illustrated a good design principle you should follow:
Ensure that zero is a valid state for all value types.
Dice only have values from 1 to 6, so to make 0 a valid state we add one on the output.
![Page 31: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/31.jpg)
Avoiding all of this
The previous example can be made trivial and avoid all of this by simply requiring that die1 always be greater than die2 in the implementation.Control the creation.
Works theoretically, but you may want the die to look more random for presentation purposes.
![Page 32: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/32.jpg)
Example2 – Craps class
public class DiceRoll : IEquatable<DiceRoll> {… public bool Equals(DiceRoll other) { if (other == null) return false; return (die1 == other.die1 && die2 == other.die2) || (die1 == other.die2 && die2 == other.die1); }
![Page 33: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/33.jpg)
Example2 – Craps class
public override bool Equals(object other) { if (other == null) return false; if (object.ReferenceEquals(this, other)) return true; if (this.GetType() != other.GetType()) return false; return Equals(other as DiceRoll); } …
![Page 34: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/34.jpg)
Example2 – Craps class
… public static DiceRoll RollDice() { return new DiceRoll(random.Next(0,5), random.Next(0,5)); }
The only way to create a DiceRoll now.
![Page 35: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/35.jpg)
Programming in C#Comparison (Sorting)
CSE / ECE 668Prof. Roger Crawfis
![Page 36: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/36.jpg)
Comparison
Comparing two instances of a type has many of the same properties and pitfalls as testing for equality.
Equality is generally more fussy.Only two protocols for a type providing
its own comparison:The IComparable interfaceThe > and < operators
![Page 37: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/37.jpg)
IComparable
The IComparable<T> and IComparable interfaces allow for sorting or ordering of a collection.
They are used in the Array.Sort method.public interface IComparable <T>{ int CompareTo(T other); // -1 if this < other, 0 if this == other, 1 if this > other}
5.CompareTo(7) => -1
“World”.CompareTo(“Hello”) => 1
32.CompareTo(8*4) => 0
![Page 38: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/38.jpg)
IComparable
Classes implementing IComparable areValues types like Int32, Double, DateTime,
…The class Enum as base class of all
enumeration typesThe class String
Defines a type to be is-a IComparable.
![Page 39: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/39.jpg)
The > and < operators
Value types that have a clear context independent concept of less than and greater than should implement the < and > operators.
These are compiled statically into the code, making value types more efficient.
![Page 40: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/40.jpg)
Programming in C#Changing Comparison
CSE 494R(proposed course for 459 Programming in C#)
Prof. Roger Crawfis
![Page 41: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/41.jpg)
IComparer
IComparer is used to provide pluggable (or interchangable) comparisons.
Used with a type, not part of the type.
public interface IComparer {int Compare(object x, object y); // -1 if x < y, 0 if x == y, 1 if x >
y}
IComparer implementations:Comparer, CaseInsensitiveComparer: for
string comparisons
![Page 42: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/42.jpg)
Custom IComparer
Creation of table of strings:
string[][] Table = {new string[] {"John", "Dow", "programmer"},new string[] {"Bob", "Smith", "agent"},new string[] {"Jane", "Dow", "assistant"},new string[] {"Jack", "Sparrow", "manager"}
};
Printing the table:
foreach (string[] Row in Table) {Console.WriteLine(String.Join(", ", Row));
}
![Page 43: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/43.jpg)
Custom IComparer
Comparer for single table (array) column:class ArrayComparer<T> : IComparer<T[]> where T : IComparable<T> {
private int m_Index;public ArrayComparer(int index) {
this->index = index;}public int Compare(T[] x, T[] y) {
return x[index ].CompareTo( y[index ] );}
} Printing the table:
Array.Sort(Employees, new ArrayComparer<string>(2));
foreach (string[] Row in Employees) {Console.WriteLine(String.Join(", ", Row));
}
Bob, Smith, agentJane, Dow, assistantJack, Sparrow, managerJohn, Dow, programmer
![Page 44: Programming in C# Equality](https://reader035.vdocuments.net/reader035/viewer/2022070420/56815f3f550346895dce148a/html5/thumbnails/44.jpg)
Custom IComparer
Implement IComparable and IComparable<T> public interface IComparable {
int CompareTo(object obj); // -1: this < obj, 0: this == obj, 1: this > obj
}
public interface IComparable<T> {int CompareTo(T obj); // -1: this < obj, 0: this == obj, 1: this > obj
}
class Fraction : IComparable, IComparable<Fraction> {private int n, d;
public int CompareTo(object o) {return CompareTo((Fraction) o);
}
public int CompareTo(Fraction f) {return n*f.d – f.n*d
}}