Game Programming
Style & Design Principles
Nick Prühs
Objectives
• To get an idea of good code style and structure in general
• To understand the importance of consistent naming and code
conventions
• To learn how to property design types and members
2 / 61
What is “good code”?
• Straight-forward and obvious
 You’ll spend far more time reading code than writing.
 You’ll spend much time reading code you didn’t write.
• Loose coupling
• Well tested
• Reused code
• Efficient (?)
3 / 61
How to achieve “good code”?
Source: http://xkcd.com/844/ 4 / 61
How to achieve “good code”?
• Pair programming
• Code Reviews
• Client-first programming, Test-driven development
• Refactoring
• Unit Testing
• Great tools
• Static code analysis
5 / 61
Naming Conventions
• Greatly increase readability and usability
• Differ from language to language
 We’ll take a look at C#, but many guidelines apply to other
languages as well.
6 / 61
C# Capitalization
• PascalCasing
 Namespaces
 Types
 Member Names
• camelCasing
 Method Parameter Names
7 / 61
Capitalization of Acronyms
• Both characters of two-character acronyms
 Example: IO
• Only first character of three-character acronyms
 Example: Xml, Html
• Never for camelCased identifiers (such as parameters)
8 / 61
Word Choice
• Easily readable:
 HorizontalAlignment instead of AlignmentHorizontal
• Favor readability over brevity:
 CanScrollHorizontally instead of ScrollableX
• No underscores
• No Hungarian notation
• No abbreviations
 Abbreviations don’t work well with IDEs.
• Semantic names
 GetLength instead of GetInt
9 / 61
Namespace Names
<Company>.<Product>.<Feature>.<SubNamespace>
• Plural names where appropriate
 Example: System.Collections
• Don’t use the same name for namespace and type
• Don’t use common names such as Element, Component
10 / 61
Type Names
• Nouns for classes and structs
 Example: List, Vector
• Derived classes can end with name of base class
 Example: ArgumentException
• Adjectives for interfaces
• Prefix interface names with ‘I’
 Example: IComparable
• Use descriptive names for type-parameters
 Dictionary<TKey, TValue> instead of Dictionary<K, V>
• Singular names for non-flags enums
 Color instead of Colors
11 / 61
Member Names
• Verbs for methods and events
 Example: AddComponent, ComponentAdded
• Nouns or adjectives for fields
 Example: Duration, Invulnerable
• Nouns for properties
• Plurals for collections
 Items instead of ItemList
12 / 61
Boolean Trap #1
• Be positive!
 Enabled = true instead of Disabled = false
 CaseSensitive = true instead of CaseInsensitive = false
13 / 61
Tabs vs. Spaces
• Holy war between two fractions of programmers
• Some are for tabs…
 Seperation of visual representation from data
 Customizable
 Faster
 Specifically meant for indentation
• … while others are for spaces.
 Always one column
 Supported by IDEs anyway
14 / 61
Tabs vs. Spaces
Most important rule:
Stay consistent.
… or it might even blow up your version control.
15 / 61
Tabs vs. Spaces
„That said, only a moron would use tabs to format their code.”
- Jeff Atwood
16 / 61
Type Design – Class vs. Struct
Make a type a struct instead of a class if it…
• Logically represents a single value,
• Has a valid state if all data is set to zero,
• Has an instance size < 16 bytes,
• Is Immutable, and
• Won’t have to be boxed frequently
17 / 61
Type Design – Class vs. Struct
Make a type a struct instead of a class if it…
• Logically represents a single value,
• Has a valid state if all data is set to zero,
 This is the case for array initialization, for example.
• Has an instance size < 16 bytes,
• Is Immutable, and
• Won’t have to be boxed frequently
18 / 61
Type Design – Class vs. Struct
Make a type a struct instead of a class if it…
• Logically represents a single value,
• Has a valid state if all data is set to zero,
• Has an instance size < 16 bytes,
 Value type assignments copy all values
• Is Immutable, and
• Won’t have to be boxed frequently
19 / 61
Type Design – Class vs. Struct
Make a type a struct instead of a class if it…
• Logically represents a single value,
• Has a valid state if all data is set to zero,
• Has an instance size < 16 bytes,
• Is Immutable, and
 Passing (and returning) by value implicitly creates a copy
 Value types that can be changed will be confusing!
• Won’t have to be boxed frequently
20 / 61
Type Design – Class vs. Struct
Make a type a struct instead of a class if it…
• Logically represents a single value,
• Has a valid state if all data is set to zero,
• Has an instance size < 16 bytes,
• Is Immutable, and
• Won’t have to be boxed frequently
 Happens when they’re cast to a interface
 Allocated on heap and garbage collected, then!
21 / 61
Type Design – Class vs. Interface
• Use interfaces for polymorphic value type hierarchies
 Example: IComparable, IConvertible
• If an interface does more than exactly one thing, that’s a warning
sign.
22 / 61
Type Design – Class vs. Interface
23 / 61
Type Design – Class vs. Interface
• Favor (abstract) classes over interfaces
 Adding members to an interface is a breaking change!
 Seperating of contract from implementation with an interface is a
myth…
 … whereas doing so with an abstract class is not.
24 / 61
Type Design – Enums
• Favor enums over static constants
• Provide a zero value for simple enums (e.g. None)
• Don’t use enums for open sets
• Don’t use flag enums if some combinations can be invalid
25 / 61
Member Design – Property vs. Method
Make a member a method instead of a property if it…
• Is significantly slower than a field access would be,
• Is a conversion operation (such as ToString),
• Returns a different result each time it is called,
• Has an observable side effect,
• Returns a copy of an internal state, or
• Returns an array
26 / 61
Member Design – Properties
• Preserve previous value if a property setter throws an exception
• Don’t throw exceptions from property getters.
 Use a method in that case.
27 / 61
Gotcha!
Don‘t throw exceptions from
static constructors!
28 / 61
Member Design – Methods
• Shorter overloads should simply call longer ones
 public int IndexOf(string s)
 public int IndexOf(string s, int startIndex)
 public int IndexOf(string s, int startIndex, int count)
• Use descriptive parameter names
• Avoid varying names or inconsistent ordering
• Make only the longest overload virtual
• Allow null to be passed for optional parameters
 Default arguments are not CLS-compliant!
29 / 61
Member Design – Extensions
Use an extension method, if ...
• It adds functionality relevant to every implementation of an interface,
or
• An instance method would introduce unwanted dependencies
30 / 61
Member Design – Operator Overloads
• Make sense for types that feel like primitive types
• Should be defined symmetrically
• Provide methods with friendly names, as well
 Some languages don‘t support operator overloads.
31 / 61
Member Design – Parameters
• Use the least derived required paramter type
• Validate all arguments
• params keyword is useful for arbitrary (but small) numbers of
parameters
 Pay attention on parameter order
 Be aware that null is a valid params array argument
32 / 61
Boolean Trap #2
Guess what this code means?
widget.repaint(false);
33 / 61
Boolean Trap #2
Guess what this code means?
widget.repaint(false);
• Function argument is called immediate
 true means immediate painting
 false means deferred painting
• Better use method overloads or enums!
34 / 61
Boolean Trap #3
Guess what this code means?
var opacitySlider = new Slider(true);
35 / 61
Boolean Trap #3
Guess what this code means?
var opacitySlider = new Slider(true);
• Function argument is called horizontal
 true means horizontal slider
 false means vertical slider
• Better use subclasses!
 „Heck, who knows someday you’ll need a
diagonal slider!”
36 / 61
Boolean Trap #4
Guess what this code means?
stackView.updateHeight(false);
37 / 61
Boolean Trap #4
Guess what this code means?
stackView.updateHeight(false);
• Immediate or not?
• Don‘t update height?
• Update width???
38 / 61
Boolean Trap #5
The more, the merrier!
event.initKeyEvent("keypress", true, true,
null, null, false, false, false, false, 9,
0);
39 / 61
Exceptions
• Help you deal with any unexpected or exceptional situations that
occur when a program is running
• Exceptions are types that all ultimately derive from
System.Exception.
• Generated by the common language runtime (CLR), by the .NET
Framework or any third-party libraries, or by application code.
40 / 61
Benefits of Exceptions
• Integrate well with object-oriented languages
 Does not require changing the method signature
• Can’t be ignored, whereas error codes can
• Exception objects contain detailed information about the error, such
as the state of the call stack and a text description of the error.
• Possibility of defining an Unhandled Exception Filter (UEF)
• Supported by debuggers
41 / 61
Gotcha!
Don’t return error codes!
42 / 61
Exception Design
• Exceptions provide consistent error reporting
 No bool return value required
 No HRESULT
 No GetLastError
• Don’t return error codes.
 If your error code is for the developer, add additional information
to the exception message instead.
 If your error code is for the exception handler, add a new
exception type instead.
43 / 61
Throwing Exceptions
• Throw the most specific exception that makes sense.
 ArgumentException
 ArgumentNullException
• Provide rich and meaningful error messages.
• Introduce a new exception type only if it should be handled
differently than existing exception types.
44 / 61
Handling Exceptions
• Use a try block around the statements that might throw exceptions.
• Once an exception occurs in the try block, the flow of control jumps
to the first associated exception handler that is present anywhere in
the call stack.
• If no exception handler for a given exception is present, the program
stops executing with an error message.
45 / 61
Handling Exceptions
Unhandled exception reported by Microsoft Visual Studio 2012
46 / 61
Exception Best Practice
47 / 61
Exception Best Practice
• Do not catch an exception unless you can handle it and leave the
application in a known state.
• Do not catch non-specific exceptions, such as System.Exception.
• Use a finally block to release resources, for example to close any
streams or files that were opened in the try block.
48 / 61
Gotcha!
Never use empty catch blocks!
49 / 61
Common Exceptions
• Thrown by your application or the framework
 InvalidOperationException
 ArgumentException
 ArgumentNullException
 ArgumentOutOfRangeException
• Thrown by the CLR
 NullReferenceException
 IndexOfOutRangeException
 StackOverflowException
 OutOfMemoryException
50 / 61
Collection Design
• Inherit from Collection<T>, ReadOnlyCollection<T> or
KeyedCollection<TKey, TItem>
• Implement IEnumerable<T>
 Allows foreach iteration of your collection.
• Implement ICollection<T> or IList<T> where it makes sense
• Implement an indexer where it makes sense
51 / 61
Gotcha!
Don’t implement ICloneable!
52 / 61
The interface ICloneable
• Contract doesn’t define whether cloning an object returns a deep or
shallow copy
• Define your own Clone method if required, without implementing the
interface
53 / 61
Hint
Implement IEquatable<T>
on value types!
54 / 61
The interface IEquatable
• Defines a method for determining equality of object instances
• Implement on value types to prevent boxing
• Override Object.Equals, == and != operators as well
55 / 61
Hint
Implement IComparable<T>
where it makes sense!
56 / 61
The interface IComparable
• Defines a type-specific comparison method for ordering or sorting
type instances.
• Override <, >, <= and >= operators as well
57 / 61
Equals, GetHashCode & ToString
• If Object.Equals returns true for any two objects, GetHashCode must
return the same value for these objects.
 Dictionaries, which are implemented as Hashtables in .NET, will
place two equal objects in different buckets otherwise, and
lookup might fail in that case.
• Override ToString wherever it makes sense.
 Provides more useful debug output.
58 / 61
Gotcha!
GetHashCode must return the
exactly same value across the
lifetime of an object!
59 / 61
References
• Stack Overflow. What does a good programmer's code look like?
http://stackoverflow.com/questions/366588/what-does-a-good-
programmers-code-look-like, April 2017.
• Cwalina, Abrams. Framework Design Guidelines. 2nd Edition. Addison-
Wesley, 2011.
• Hidayat. hall of api shame: boolean trap.
http://ariya.ofilabs.com/2011/08/hall-of-api-shame-boolean-trap.html,
August 24, 2011.
• Atwood. Death to the Space Infidels!
http://www.codinghorror.com/blog/2009/04/death-to-the-space-
infidels.html, April 13, 2009.
• MSDN. System Namespace. https://msdn.microsoft.com/en-
us/library/System(v=vs.110).aspx, April 2017.
60 / 61
Thank you!
http://www.npruehs.de
https://github.com/npruehs
@npruehs
nick.pruehs@daedalic.com
5 Minute Review Session
• Name a few characteristics of good code!
• How can you achieve good code?
• Tabs or spaces?
• When should you use a struct instead of a class?
• When should you use a method instead of a property?
• Name the three common interfaces and base classes that can be used
for collections in .NET!
• What is the main purpose of the interface IEquatable?
• What is the main purpose of the interface IComparable?
• How are Equals and GetHashCode related to each other?

Game Programming 04 - Style & Design Principles

  • 1.
    Game Programming Style &Design Principles Nick Prühs
  • 2.
    Objectives • To getan idea of good code style and structure in general • To understand the importance of consistent naming and code conventions • To learn how to property design types and members 2 / 61
  • 3.
    What is “goodcode”? • Straight-forward and obvious  You’ll spend far more time reading code than writing.  You’ll spend much time reading code you didn’t write. • Loose coupling • Well tested • Reused code • Efficient (?) 3 / 61
  • 4.
    How to achieve“good code”? Source: http://xkcd.com/844/ 4 / 61
  • 5.
    How to achieve“good code”? • Pair programming • Code Reviews • Client-first programming, Test-driven development • Refactoring • Unit Testing • Great tools • Static code analysis 5 / 61
  • 6.
    Naming Conventions • Greatlyincrease readability and usability • Differ from language to language  We’ll take a look at C#, but many guidelines apply to other languages as well. 6 / 61
  • 7.
    C# Capitalization • PascalCasing Namespaces  Types  Member Names • camelCasing  Method Parameter Names 7 / 61
  • 8.
    Capitalization of Acronyms •Both characters of two-character acronyms  Example: IO • Only first character of three-character acronyms  Example: Xml, Html • Never for camelCased identifiers (such as parameters) 8 / 61
  • 9.
    Word Choice • Easilyreadable:  HorizontalAlignment instead of AlignmentHorizontal • Favor readability over brevity:  CanScrollHorizontally instead of ScrollableX • No underscores • No Hungarian notation • No abbreviations  Abbreviations don’t work well with IDEs. • Semantic names  GetLength instead of GetInt 9 / 61
  • 10.
    Namespace Names <Company>.<Product>.<Feature>.<SubNamespace> • Pluralnames where appropriate  Example: System.Collections • Don’t use the same name for namespace and type • Don’t use common names such as Element, Component 10 / 61
  • 11.
    Type Names • Nounsfor classes and structs  Example: List, Vector • Derived classes can end with name of base class  Example: ArgumentException • Adjectives for interfaces • Prefix interface names with ‘I’  Example: IComparable • Use descriptive names for type-parameters  Dictionary<TKey, TValue> instead of Dictionary<K, V> • Singular names for non-flags enums  Color instead of Colors 11 / 61
  • 12.
    Member Names • Verbsfor methods and events  Example: AddComponent, ComponentAdded • Nouns or adjectives for fields  Example: Duration, Invulnerable • Nouns for properties • Plurals for collections  Items instead of ItemList 12 / 61
  • 13.
    Boolean Trap #1 •Be positive!  Enabled = true instead of Disabled = false  CaseSensitive = true instead of CaseInsensitive = false 13 / 61
  • 14.
    Tabs vs. Spaces •Holy war between two fractions of programmers • Some are for tabs…  Seperation of visual representation from data  Customizable  Faster  Specifically meant for indentation • … while others are for spaces.  Always one column  Supported by IDEs anyway 14 / 61
  • 15.
    Tabs vs. Spaces Mostimportant rule: Stay consistent. … or it might even blow up your version control. 15 / 61
  • 16.
    Tabs vs. Spaces „Thatsaid, only a moron would use tabs to format their code.” - Jeff Atwood 16 / 61
  • 17.
    Type Design –Class vs. Struct Make a type a struct instead of a class if it… • Logically represents a single value, • Has a valid state if all data is set to zero, • Has an instance size < 16 bytes, • Is Immutable, and • Won’t have to be boxed frequently 17 / 61
  • 18.
    Type Design –Class vs. Struct Make a type a struct instead of a class if it… • Logically represents a single value, • Has a valid state if all data is set to zero,  This is the case for array initialization, for example. • Has an instance size < 16 bytes, • Is Immutable, and • Won’t have to be boxed frequently 18 / 61
  • 19.
    Type Design –Class vs. Struct Make a type a struct instead of a class if it… • Logically represents a single value, • Has a valid state if all data is set to zero, • Has an instance size < 16 bytes,  Value type assignments copy all values • Is Immutable, and • Won’t have to be boxed frequently 19 / 61
  • 20.
    Type Design –Class vs. Struct Make a type a struct instead of a class if it… • Logically represents a single value, • Has a valid state if all data is set to zero, • Has an instance size < 16 bytes, • Is Immutable, and  Passing (and returning) by value implicitly creates a copy  Value types that can be changed will be confusing! • Won’t have to be boxed frequently 20 / 61
  • 21.
    Type Design –Class vs. Struct Make a type a struct instead of a class if it… • Logically represents a single value, • Has a valid state if all data is set to zero, • Has an instance size < 16 bytes, • Is Immutable, and • Won’t have to be boxed frequently  Happens when they’re cast to a interface  Allocated on heap and garbage collected, then! 21 / 61
  • 22.
    Type Design –Class vs. Interface • Use interfaces for polymorphic value type hierarchies  Example: IComparable, IConvertible • If an interface does more than exactly one thing, that’s a warning sign. 22 / 61
  • 23.
    Type Design –Class vs. Interface 23 / 61
  • 24.
    Type Design –Class vs. Interface • Favor (abstract) classes over interfaces  Adding members to an interface is a breaking change!  Seperating of contract from implementation with an interface is a myth…  … whereas doing so with an abstract class is not. 24 / 61
  • 25.
    Type Design –Enums • Favor enums over static constants • Provide a zero value for simple enums (e.g. None) • Don’t use enums for open sets • Don’t use flag enums if some combinations can be invalid 25 / 61
  • 26.
    Member Design –Property vs. Method Make a member a method instead of a property if it… • Is significantly slower than a field access would be, • Is a conversion operation (such as ToString), • Returns a different result each time it is called, • Has an observable side effect, • Returns a copy of an internal state, or • Returns an array 26 / 61
  • 27.
    Member Design –Properties • Preserve previous value if a property setter throws an exception • Don’t throw exceptions from property getters.  Use a method in that case. 27 / 61
  • 28.
    Gotcha! Don‘t throw exceptionsfrom static constructors! 28 / 61
  • 29.
    Member Design –Methods • Shorter overloads should simply call longer ones  public int IndexOf(string s)  public int IndexOf(string s, int startIndex)  public int IndexOf(string s, int startIndex, int count) • Use descriptive parameter names • Avoid varying names or inconsistent ordering • Make only the longest overload virtual • Allow null to be passed for optional parameters  Default arguments are not CLS-compliant! 29 / 61
  • 30.
    Member Design –Extensions Use an extension method, if ... • It adds functionality relevant to every implementation of an interface, or • An instance method would introduce unwanted dependencies 30 / 61
  • 31.
    Member Design –Operator Overloads • Make sense for types that feel like primitive types • Should be defined symmetrically • Provide methods with friendly names, as well  Some languages don‘t support operator overloads. 31 / 61
  • 32.
    Member Design –Parameters • Use the least derived required paramter type • Validate all arguments • params keyword is useful for arbitrary (but small) numbers of parameters  Pay attention on parameter order  Be aware that null is a valid params array argument 32 / 61
  • 33.
    Boolean Trap #2 Guesswhat this code means? widget.repaint(false); 33 / 61
  • 34.
    Boolean Trap #2 Guesswhat this code means? widget.repaint(false); • Function argument is called immediate  true means immediate painting  false means deferred painting • Better use method overloads or enums! 34 / 61
  • 35.
    Boolean Trap #3 Guesswhat this code means? var opacitySlider = new Slider(true); 35 / 61
  • 36.
    Boolean Trap #3 Guesswhat this code means? var opacitySlider = new Slider(true); • Function argument is called horizontal  true means horizontal slider  false means vertical slider • Better use subclasses!  „Heck, who knows someday you’ll need a diagonal slider!” 36 / 61
  • 37.
    Boolean Trap #4 Guesswhat this code means? stackView.updateHeight(false); 37 / 61
  • 38.
    Boolean Trap #4 Guesswhat this code means? stackView.updateHeight(false); • Immediate or not? • Don‘t update height? • Update width??? 38 / 61
  • 39.
    Boolean Trap #5 Themore, the merrier! event.initKeyEvent("keypress", true, true, null, null, false, false, false, false, 9, 0); 39 / 61
  • 40.
    Exceptions • Help youdeal with any unexpected or exceptional situations that occur when a program is running • Exceptions are types that all ultimately derive from System.Exception. • Generated by the common language runtime (CLR), by the .NET Framework or any third-party libraries, or by application code. 40 / 61
  • 41.
    Benefits of Exceptions •Integrate well with object-oriented languages  Does not require changing the method signature • Can’t be ignored, whereas error codes can • Exception objects contain detailed information about the error, such as the state of the call stack and a text description of the error. • Possibility of defining an Unhandled Exception Filter (UEF) • Supported by debuggers 41 / 61
  • 42.
  • 43.
    Exception Design • Exceptionsprovide consistent error reporting  No bool return value required  No HRESULT  No GetLastError • Don’t return error codes.  If your error code is for the developer, add additional information to the exception message instead.  If your error code is for the exception handler, add a new exception type instead. 43 / 61
  • 44.
    Throwing Exceptions • Throwthe most specific exception that makes sense.  ArgumentException  ArgumentNullException • Provide rich and meaningful error messages. • Introduce a new exception type only if it should be handled differently than existing exception types. 44 / 61
  • 45.
    Handling Exceptions • Usea try block around the statements that might throw exceptions. • Once an exception occurs in the try block, the flow of control jumps to the first associated exception handler that is present anywhere in the call stack. • If no exception handler for a given exception is present, the program stops executing with an error message. 45 / 61
  • 46.
    Handling Exceptions Unhandled exceptionreported by Microsoft Visual Studio 2012 46 / 61
  • 47.
  • 48.
    Exception Best Practice •Do not catch an exception unless you can handle it and leave the application in a known state. • Do not catch non-specific exceptions, such as System.Exception. • Use a finally block to release resources, for example to close any streams or files that were opened in the try block. 48 / 61
  • 49.
    Gotcha! Never use emptycatch blocks! 49 / 61
  • 50.
    Common Exceptions • Thrownby your application or the framework  InvalidOperationException  ArgumentException  ArgumentNullException  ArgumentOutOfRangeException • Thrown by the CLR  NullReferenceException  IndexOfOutRangeException  StackOverflowException  OutOfMemoryException 50 / 61
  • 51.
    Collection Design • Inheritfrom Collection<T>, ReadOnlyCollection<T> or KeyedCollection<TKey, TItem> • Implement IEnumerable<T>  Allows foreach iteration of your collection. • Implement ICollection<T> or IList<T> where it makes sense • Implement an indexer where it makes sense 51 / 61
  • 52.
  • 53.
    The interface ICloneable •Contract doesn’t define whether cloning an object returns a deep or shallow copy • Define your own Clone method if required, without implementing the interface 53 / 61
  • 54.
  • 55.
    The interface IEquatable •Defines a method for determining equality of object instances • Implement on value types to prevent boxing • Override Object.Equals, == and != operators as well 55 / 61
  • 56.
  • 57.
    The interface IComparable •Defines a type-specific comparison method for ordering or sorting type instances. • Override <, >, <= and >= operators as well 57 / 61
  • 58.
    Equals, GetHashCode &ToString • If Object.Equals returns true for any two objects, GetHashCode must return the same value for these objects.  Dictionaries, which are implemented as Hashtables in .NET, will place two equal objects in different buckets otherwise, and lookup might fail in that case. • Override ToString wherever it makes sense.  Provides more useful debug output. 58 / 61
  • 59.
    Gotcha! GetHashCode must returnthe exactly same value across the lifetime of an object! 59 / 61
  • 60.
    References • Stack Overflow.What does a good programmer's code look like? http://stackoverflow.com/questions/366588/what-does-a-good- programmers-code-look-like, April 2017. • Cwalina, Abrams. Framework Design Guidelines. 2nd Edition. Addison- Wesley, 2011. • Hidayat. hall of api shame: boolean trap. http://ariya.ofilabs.com/2011/08/hall-of-api-shame-boolean-trap.html, August 24, 2011. • Atwood. Death to the Space Infidels! http://www.codinghorror.com/blog/2009/04/death-to-the-space- infidels.html, April 13, 2009. • MSDN. System Namespace. https://msdn.microsoft.com/en- us/library/System(v=vs.110).aspx, April 2017. 60 / 61
  • 61.
  • 62.
    5 Minute ReviewSession • Name a few characteristics of good code! • How can you achieve good code? • Tabs or spaces? • When should you use a struct instead of a class? • When should you use a method instead of a property? • Name the three common interfaces and base classes that can be used for collections in .NET! • What is the main purpose of the interface IEquatable? • What is the main purpose of the interface IComparable? • How are Equals and GetHashCode related to each other?