Welcome to the c# master class notes. Nothing here but lovingly crafted notes for personal use. Summarized and formatted by yours truly. A highly recommended C# Masterclass - Harsha Vardhan for anyone interested in c#.
-
What π€: .net it is not a single language. It's an application dev platform to build desktop, web and mobile. Calculator, Supermarket app (may or may not integrate with web) standalone. Web apps live on a server and usually interact with a database. Mobile apps are installed on phones. It is a technology to develop applications. It's a free development platform since 2002. They will not execute directly on the operating system. It runs on a component called CLR. .net supports more than 30 languages. C#, VB, VC++ etc.
-
Why π€·ββοΈ: Secured execution environment π Wide variety of applications and languages π Code reusability π
Modules and apps π¦:
ASP.net Websites (static collection of websites), Web Applications (dynamic web content), Web Services (Reusable programs on the web) C#.net Windows GUI apps, Windows Services (no GUI components), Console Applications (CLI apps in command prompt window)
These must be installed on the user machine π»
c# application source is written by dev π§βπ» Ultimately we need to convert to "Native Machine Code" π₯οΈ Intermediate languages are (Language Neutral) π CLR is an execution language (Execution Engine of all .net modules) π Native Machine code is operating system-specific language (Executed by OS) π» c# application source code -> compiler -> Intermediate language -> Common Language Runtime -> Native Machine Code
In VS you can hit the start button to compile to IL. So for c#, we have a c# compiler
You can open the project in file explorer and check out an .exe
You can see the source of this by typing the command ildasm
intermediate language disassembler located in /bin/debug
THis is the most important componenet in .net
CLR: "Execution environment" for all .net languages. Code execution env that executes all types of .net Applications develoier in any .net language runes based on "CLR" only CLR is part of .net framework pre-installed on windows
Class Loader, Exception Manager, Memory Manager, Garbage collector, JIT Compiler, Thread Manager, Security Manager
- Class Loader
- Loading classes from compiled source code to memory
- Loads a class, when it is needed (before creating objec)
- Memory Manager
- Allocating necessary memory for objects
- When an object is created in teh code, certain amount of memory will be allocated for the oject in applications "heap"
- Garbage Collector
- Freeing (deleting) memory of objects
- Identifies all unreferenced objects adn delete them in memory (RAM)
- JIT (Just In Time) compiler
- Convert the MSIL code into Native Machine Language
- Compiles the code of a class when it is needed (before executing that particular class)
- Exception manager
- Raise notifications while run-rime errors
- Creates exception logs
- Thread Manager
- Create threads (background process) to exectute the code
- Entire program is treated as "Main thread"
- Developer can create sub threads (child threads) to do background processes
- Security manager
- Verifies whether the appliation has permision to access resource or not
- Before execturing the app it verifies whether the application has not attacked by malicius programs & has necessary permissins to access files / folders and hardware resources
Is a combo of Common Language Runtime (Runtime engine) + Framework Class Library (OOP collection of classes used to develop apps)
Learning about availbable components can be found in the .net framework arhciteture.
CLS (Common Language Specification)
FCL is comprised of: Win forms, ASP.net, WPF, ADO.et, Base Class Library, Common Language Runtime
Contains a set of predefined classes that can be used in all types of .net applications & langugages. for general I/O operations, type conversion, creation of threads etc.
Ex:
- Console
- String
- StringBuilder
- Convert
- Thread
- Task
- etc
Contains a set of pre-defined classes that can be used in all types of .net applications & languages for connecting to databases, inserting, updating, deleting.
Ex:
- SqlConnection
- SqlCommand
- SqlDataAdapter
- SqlDataReader
- etc
Contains a set of pre-defined classes taht can be used in windows gui applications for dev of gui elements such as form, textboc, button, checkbox etc
Ex:
- Form
- Label
- Button
- TextBox
- etc
Contains a set of prededifined classes that can be used in Ricch Windows GUI applications for dev of GUI elements. Such as window, textbox, button, checkbox, radio button
Ex:
- Window label
- Button
- Textbox
- etc
Contains a set of pre-defined calsses that can be used in Web Apps for dev of gui elements, such as page, textbox, button, checkbox, radio button etc
Ex:
- Page
- Label
- Button
- TextBox
- etc
Contains a set of rules (concepts) that are common to all .net languages such as c#.net, Vb.net etc. By following practical implementation you may not need to worry too much about this.
- Common rules of CLS:
- CTS (Common type system) : Contains datatypes such as Int32, Int64, Single, Double etc
- Classes & objects
- Reference Variables
- Method Parameters
- Generics
These datatypes help fluid translation between .net languages
.net 4.6.1 core
SHA-2, Elliptic Curve Cryptography API 64 bit JIT compiler for managed code Introduction of .NET Core
- (Multiplatoform support for windows, LINUX,Mac, Android, IOS)
We can say .net has two flavors .net framework (only windows) and .net core
- Introduced in 2016
- Microsofts application dev platofrom t for any desktop, mobile and embedded (iot)
- Supports Various OS
- Versions 1.0 - 3.1
- Mainly used in Xamarin & ASP.net Core
- Open Source (via MIT licence)
- Contains the class library which is a subset of .NET Framework
- Doesnt contain any windows specific classes / run time services
- c# / vb.net can be used for writing code of .NET Core
Visual Studio 2015 supports .NET framework 4.6.1
Visual Studio 2022 supports (2021) πΉ .NET framework 4.6.1 - 4.6 πΉ .NET Core 2.1, 3.1 πΉ .NET 5.0 - 6.0
πΈ What: Developed in 2002 for COnsole Apps, WIndows GUI and Windows Services.
πΈ Features:
- OOP Langguage
- Case Sensitve
- Strongly Typed Lang
- Compiler based lang
- Compiled based on CLI, executed by CLR
- Developed by Anders Hejlsberg
πΈ Objects:
- Things that exist in the real world (small unit) or entity
πΈ Classes:
2 types class based or prototype
- c# is a class based oop language
- A class is a model of objects
- class isnt a collection of objects
πΈ Methods:
- Are a collection of statements to perform certain operations
- Are not stored in the object but associated
Is a collections of classes, made for grouping: All classes for student, lecturer, staff may belong to a namespace called university
We can access like namespace.class
or a more pracitcal example university.student
Namespaces are included on top usually
The list of words available in a language: Keywords, Operators, Literals (), Identifiers ()
πΈ Keywords: always lower case
πΈ Operators: see language tokens
πΈ Literals: Are fixed values: String, Bool( true or false) , integer etc. Requires no declaration
Character literals should be in '' like
'a', '\''
String literals should be in ""
πΈ Identifiers: All types of names (variable names, class names, field names, property names, method names etc)
πΉ Latest versions are 9 / 10
πΉ 5.0
- Async and Await
πΉ 7.2
- private protected access modifier
- in parameter modifier
πΉ 7.3
- ==
πΉ 8.0:
-
Readonly structs
-
Switch expressions
-
Using Declarations
-
Static Local Functions
-
Default interface methods.
-
Nullable reference types.
-
Pattern matching enhancements.
-
Asynchronous streams / Asynchronous disposable.
-
Using declarations.
-
Enhancement of interpolated verbatim strings.
-
Null-coalescing assignment.
-
Static local functions.
πΈ camelCase: For all local variables and parameters
πΈ PascalCase: For all class names, structure names, namespace names, field names, method names, property names.
πΈ IPascaleCase: For all interface names
πΈ _camelCase: For all private fields
For practice purposes:
πΈ community version will suffice πΈ Professional requires a licence fee πΈ Enterprise is for large businesses of < 100 employees
For this course we only need VS Community version with component
- .NET desktop development & ASP.NET and web development may also be usefull
Visit also individual components its a good idea to include all .NET runtime & .NET Framework dev tools, sdk etc so you can test further.
- Select all up to .NET framework 4.8 targeting pack (This is optional but usefull).
Its nice to also pin to taskbar
Before you can use you will need to sign in with a microsoft account
Note the Main
method inside a class
Main method should always be static void
static
Static, in C#, is a keyword that can be used to declare a member of a type so that it is specific to that type. The static modifier can be used with a class, field, method, property, operator, event or constructor.
void
means that the method doesnt return any value to the caller
class Sample {
static void Main()
{
System.Console.WriteLine("Hi im in console");
System.Console.ReadKey();
}
}
Namespace.Classname.Method
System is a namepsace
It is a static class
Console class is Base Class Library
writeLine
goes to next line automatically
Clear
empties the console
ReadLine
accepts a string from keyboard, always returns string, even numbers are treated as string
Vary Able Are a named memory location in ram. All variables stored in stack every method call a new Stack variables value can be changed The variables must be declared before usage You cant change the type after execution
- Declaration
DataType VariableName;
- with init
DataType VariableName = value
- - overwritingVariableName = newvalue;
- - getting value
VariableName
You cant redeclare variable that is reseved as keyword
Basically Predifined types.
A type specifies what type of value you want to store in memory. There are two two types: Primitive and non primitive.
πΈ Primitive: (byte, short, int, bool)
- Stictly a single value
- building blocks of none primitive types
πΈ Non-Primitive: (strings, classes)
-
Stores one or more values
-
Usually contains multiple members
-
Sbyte = Small Byte is (0) by default
-
Byte 8 bit un-signed int meaning it cant be negative
-
Short is 16 bit and Ushort (again ushort is unsigned)
8 Bits is equal to 1 byte
-
int is a large negative to positive number. you can get min value like int.MinValue | uint mostly same as int but unsigned meaining only positive
-
long are 8 bytes of memory (very large datatype)
-
float supports decimal types. default value is:
0F
-
double is a huuge floating point number default:
0D
-
decimal with a lot of decimal place can prefix with
0M
to define this datatype -
char supports all language characters. default:
\0
or null -
string (non prmitive) collection of unicode chars default value: null
-
bool either (true/false) default:
false
You can access the default value of a type
default(int)
- Are a symbols to perform operation
- Recieve one or more operands (values) and returns one value
Classification:
- Arithmetical (+,-,*)
- Assignment (=, +=,*=)
- Increment/Decrement (n++,++n,n--)
- Comparison (==, !=,<,>)
- Logical (&,&&,|,||,!,^(Logical Exclusive))
- Concat ("string"+"string2")
- Ternary (?:,)
Post incrementation n++ (first return value then increment) Operators have a preference or order of evalutaion
Control statements are used to control the program execution flow. Used to make the execution flow jump forward
Conditional:
- if(simple-if, if-else, else-if,nested-if)
- switch-case
Looping:
- while
- do-while
- for
Jumping:
- goto
- break
In C#, the break statement is used to terminate a loop(for, if, while, etc.) or a switch statement on a certain condition.
In C#, the continue statement is used to skip over the execution part of the loop
The C# goto statement is also known jump statement. It is used to transfer control to the other part of the program
Not safe to use without an end condition
For small and large projects Object is a single unit
class car {
string regNo;
string carModel
int calculcateEmi(int carPrice, int nuOfMonths,in interestRate){
}
}
PascalCase for ClassNames
- Fields,
- Methods,
- Constructors,
- Properties,
- Events, (raise notification to other classes)
- Deconstructors (clear unmanaged resources)
Can be:
- Internal (accesible within same assembly)
- Public (accesible in same and other assembly) ie: Internal class
Compiled source of a project is called an assembly (.exe) in form of Intermediate language
By default accessors are Internal
- Static (belongs to the type itself rather than to a specific object)
- Abstract (the thing being modified has a missing or incomplete implementation)
- Sealed (cant be inherited)
- Partial (multiple partial classes that have same name are combined)
These are all optional
-
Add Class: In vs solution->add a new project and select class library.net framework. we can now access inside the class library from console app
-
Reference: We can to add a reference to our second class. By going to our top class and selecting add->reference projects and select your library
You can see on the right hand side all the main classes references.
Second class is not as accessible so we can set this to
internal
The stack references variables from the heap Objects are stored in the heap which is a grouping of all initialized objects
Since the object is nameless we need to create a reference.
ClassName referenceVariable;
class Sample {
static void Main(){
Customer c; //referenceVariable
}
}
Default values of references are null
new Classname()
class Sample {
static void Main(){
//referenceVariables: (all local by default)
Customer c1, c2;
//Objects: Contain whatever properties we have defined in Customer
new c1 = Customer(); //All these objects get stored in the heap only
new c2 = Customer(); //2nd object
// Note these new objects are nameless. For this we need the reference variable
}
}
- Object is representaiton of a thing
- All objects created based on classes
- Foreach app exec a new heap will be created
- All reference variables (local variables of methods) are stored in the stack
- Foreach mehtod call a new will stack will be created
- Method is collection of statements to perform functions
- Single values should be stored as field
- Class supports internal / public modifiers
- Class supports 4 modifiers: static, abstract, sealed and partial
- Object stores actual data (group of fields) and can access methods of a class
- A reference variable stores address for example (just one object)
- Good for operations that don't require any data from an instance of the class (from this ) and can perform their intended purpose solely using their arguments
- To keep a count of the number of objects that have been instantiated
- To store a value that must be shared among all instances
- To use as a unit of organization for methods not associated with particular objects like
static void Main()
Is a variable stored inside an object Isolated for each object
Field Creation Syntax:
accessModifier modifier type Fieldname
public int proudctID
Static: Const: Readonly:
A good practice is to define all re-useable classes inside the class library
Aka Access Specifier or Visibility Modifier.
public class DomesticProduct:Product{
}
Are stored outside the object
Accessible through classname (not object)
Example: Math.Round()
Are common to all objects ie:
class BankAccount {
//instance fields
long accountNumber;
string accountHolderName;
double currentBalance;
static string bankName; // Note this will not be stored in the heap
}
bankName = "Bank Of Joe";
accountNumber:1001 accountHolderName:"Bill" currentBalance: 4000
accountNumber:1002 accountHolderName:"Steve" currentBalance: 5001
- To use as a unit of organization for methods not associated with particular objects like
static void Main()
π’ Is a variable stored inside an object π’ Isolated for each object
Field Creation Syntax:
accessModifier modifier type Fieldname
public int proudctID
πΌοΈ Static: π Const: π Readonly:
A good practice is to define all re-useable classes inside the class library
π Aka Access Specifier or Visibility Modifier.
public class DomesticProduct:Product{
}
π Are stored outside the object
π Accessible through classname (not object)
Example: Math.Round()
Are common to all objects ie:
class BankAccount {
//instance fields
long accountNumber;
string accountHolderName;
double currentBalance;
static string bankName; // Note this will not be stored in the heap
}
bankName = "Bank Of Joe";
accountNumber:1001 accountHolderName:"Bill" currentBalance: 4000
accountNumber:1002 accountHolderName:"Steve" currentBalance: 5001
Like static they are common to all objects of the class We cant change constant valuess
- Accessible with the class name
Product.CategoryName
- They are not stored in the object
- Constant fields are replaced directly with the value (on compilation) not stored in memory
- They must be initialized, in line with declaration (literal value only)
public const string CategoryName ="Electronics"
- Can be decared as
local
constants in a method - They cannot be referenced variables
Written like so:
AccessModifier const type FieldName= value
private const int CategoryName=Electronics
Good if: If you dont require to change this field
π’ Readonly fields are like instance fields, thate is stored in every object individually We cant chagne the value of readonly
- Readonly are accessible with reference variable (via object)
- Must be initialized either in-line with declaration or in the constructor
Written like so:
AccessModifier readonly DataType FieldName= value
private readonly int dateOfPurchase=1804291
Good if: If we have some data thats isolated to every object. At same time we dont want this value to be changed.
Access modifier is not applicable const string developerName="joey"
Fields are variables that are declared in the class but stored in object.
Access modifiers: private, protected, internal, etc.
Modifiers of fields: static, const, readonly.
Instance fields: Individual for each object; Static fields are common (one-time) for all objects.
Constants: Must be initialized along with declaration; Readonly must be initialized either 'along with declaration' or in 'instance constructor'.
(Fields Example Program.cs (ConsoleApp))[_cs\FieldExample\Program.cs] (Fields Example Product)[_cs\FieldExample\Class1.cs]
Create a console application that stores and displays 10 employees information of Harsha Inc.
Class Library: Contains Employee class. Console Application: Consumes the Employee class and creates 10 objects of Employee class.
[x] Step 1: Create a class library and console application. Add reference of class library into console application.
[x] Step 2: Create a class called Employee in the class library with fields EmpID, EmpName, SalaryPerHour, NoOfWorkingHours and NetSalary. Additionally, add the following fields:
- A static field called OrganizationName and initialize it as "Harsha Inc." in Main method.
- A constant field called TypeOfEmployee and initialize it as "Contract Based" along with declaration.
- A readonly field called DepartmentName and initialize it as "Finance Department" in constructor.
[x] Step 3: Create up to 10 objects of Employee class in the console application. Read each employee details (EmpID, EmpName, SalaryPerHour, NoOfWorkingHours) from the user (from the keyboard) using loop.
Calculate net salary (SalaryPerHour * NoOfWorkingHours).
And display the all details of the same object (including EmpID, EmpName, SalaryPerHour, NoOfWorkingHours, NetSalary, OrganizationName, TypeOfEmployee, DepartmentName) and then ask the user 'Do you want to continue to next employee'.
If the user choose 'Yes', proceed to the next employee; otherwise stop the loop.
//studying Collections, it will pay off in the long run.
The void keword is usefull when the method does not need to return anything.
methods use PascalCase
Concept of grouping data together. Keeps safe from interference.
- Fields: store data (have long lives)
- Methods manipulate data
- class is used to group these together
We can pass parameters to methods
Getters and setters demonstrate this principle
public void SetQuantityInStock(int value)
{
quantityInStock = value;
}
public double GetQuantityInStock()
{
return quantityInStock;
}
Where the class can hide data/operations from outside of the class. Like internal methods.
So classes can be divided into two different types:
Private Fields / Methods Public Fields / Methods
Parameters are stored in the stack. And created there on each method call.
Local Variables are those stored inside the method body. Short lives.
Refers to the current object of a particular method.
Only available within the instance methods.
So this refers to the object in question.
Good If: you have a parameter that is the same as a field you have to reference using this.
-
Can be called directly on class.
-
Called using static keyword.
-
Can't access instance methods.
-
Can't use this keyword.
Good if: you want to handle static the fields.
Product.GetTotalNumberOfProducts();
public static int TotalNoProducts;
public static void SetTotalNumberOfProducts(int value)
{
TotalNoProducts = value;
}
Imagine we have three objects created. We can pass these objects as references in the form of an argument.
- You can pass reference variables as an argument.
- The data type will be the class name.
int totalQuantity = Product.GetTotalQuantity(product1,product2,product3);
//static method. Passing object references as arguments
public static int GetTotalQuantity(Product product1,Product product2,Product product3)
{
int total;
total = product1.GetQuantityInStock() + product2.GetQuantityInStock() + product3.GetQuantityInStock();
return total;
}
You can omit parameter values and a default will be taken.
public void SomeMethod(int a, int b = 0)
{
//some code
}
Supply value to the parameter based on a name.
Good if: You want to change the order of parameters. Parameters are also understandable for other developers.
Like so: MethodName(Param:value,Param2:Value2)
Writing multiple methods with the same name in the class with different parameters.
Good If: You need one method to have several options while calling.
Rule: The param types of all the methods must be different.
void display() { ... }
void display(int a) { ... }
float display(double a) { ... }
float display(int a, float b) { ... }
// or
public void CalculateIva(double cost,double percentage){
//create local variable
double t;
if (cost <= 2000)
{
t = cost * 21 / 1000;
}
else
{
t = percentage * 10 / 1000;
}
//Finally store
iva = t;
}
Specify how the parameter receives a value.
double p = 7,4;
product2=CalculateTax(p);
Parameter Modifiers(List) :
- Default
- ref
- out
- in
- params
How:
- The argument will be assigned into the parameter and vice versa. (Does both)
- The argument must be a variable.
- You cannot pass literal values.
double p = 7.2;
product1.Calculate(percentage:ref p)
double q = 10;
product2.CalculateTax(ref q)
public void CalculateIva(ref double percentage){}
This out parameter will be automatically assigned at the end.
How:
- The argument will not be assigned into the parameter. But only in reverse.
- The argument must be a variable (can be uninitialized).
double p = 200.500;
product1.CalculateIva(out p);
public void CalculateIva(out double percentage){
//create local variable
double t;
percentage = 10.45;
if (cost <= 2000)
{
t = cost * 21 / 1000;
}
else
{
t = percentage * 10 / 1000;
}
//Finally store
iva = t;
}
Like normal the argument will be assigned into the parameter, but the parameter becomes readonly. Meaning you cannot modify the value of the parameter.
double p = 9.2;
product1.CalculateIva(in p);
public void CalculateIva(in double percentage){
//create local variable
double t;
// percentage = 10.45; // This will get an error as the local variable cannot be changed (read only)
if (cost <= 2000)
{
t = cost * 21 / 1000;
}
else
{
t = percentage * 10 / 1000;
}
//Finally store
iva = t;
}
This is when the reference of return variable will be assigned to receiving variable.
class Student
{
//public field
public int grade = 2;
//public method
public void PrintGrade()
{
System.Console.WriteLine("Grade: " + grade);
}
//public method with ref return
public ref int DoWork()
{
//return reference of 'grade' field
return ref grade;
}
}
class Program
{
static void Main()
{
//creating object of Student
Student s = new Student();
//call PrintGrade
s.PrintGrade();
//call DoWork
ref int g = ref s.DoWork();
//update the value of 'ref return'
g = 5;
//call PrintGrade after updating the value of 'ref return'
s.PrintGrade(); //Output: 5
System.Console.ReadKey();
}
}
How:
- All the set of arguments will be received as an array.
- The params parameter modifier can be used only for the last parameter of the method; and can be used only once for one method.
Good if: you don't know the number of the parameters you are going to receive.
class Student
{
public void DisplaySubjects(params string[] subjects)
{
System.Console.WriteLine(subjects[1]);
}
}
class Program
{
static void Main()
{
Student s = new Student();
s.DisplaySubjects("Theory of Computation", "Computer Networks", "Something Else");
}
}
These are small local functions inside a method.
class Student
{
//method
public void DisplayMarks(int marks1, int marks2, int marks3)
{
double avgMarks = getAverageMarks();
System.Console.WriteLine("Marks 1: " + marks1);
System.Console.WriteLine("Marks 2: " + marks2);
System.Console.WriteLine("Marks 3: " + marks3);
System.Console.WriteLine("Average marks: " + avgMarks);
//create local function
double getAverageMarks()
{
//create local variable of local function
double avg;
avg = (double)(marks1 + marks2 + marks3) / 3;
return avg;
}
}
}
Same as Local functions, except these cannot access local variables or parameters of containing method.
- This is to avoid accidental access of local variables or parameters.
public void DisplayMarks(int marks1, int marks2, int marks3)
{
double avgMarks = getAverageMarks(marks1,marks2,marks3);
System.Console.WriteLine("Marks 1: " + marks1);
System.Console.WriteLine("Marks 2: " + marks2);
System.Console.WriteLine("Marks 3: " + marks3);
System.Console.WriteLine("Average marks: " + avgMarks);
//create local function
static double getAverageMarks(int m1, int m2, int m3)
{
//create local variable of local function
double avg;
avg = (double)(m1 + m2 + m3) / 3;
return avg;
}
}
Note you can change the c# language version inside the config using
<LangVersion>8.0</LangVersion>
Is a method that calls itself. Useful in mathematical computations such as finding a factorial number.
public double Factorial(int number)
{
if(number == 0)
{
return 1;
} else
{
return number * Factorial(number - 1);
}
}
The lower numerical type can be automatically (implicitly) be converted into higher numerical type.
Implicit: sbyte a = 10, int b; b=a;
or char a = 'A', int b; b=a; // b = 5
Note: double, decimal, bool and string cannot be converted implicitly
We manually convert from one data type to another by specifying a data type within brackets.
Loosey Conversion if destination type is not sufficient enough to store the converted value the value may loose.
Syntax:
(DestinationDataType)SourceValue or (double)somenumber
When:
- This is can be used at all times
- Child Class to Parent Class
The string value can be converted to any numerical data type by using the "Parsing" technique. String->int
- The source must contain digits only; no spaces, alphabets or special characters.
- If the source is invalid it raises a format exception Syntax:
DestinationDataType.Parse(SourceValue)
IF your not sure about your characters its recomended to use Try parse.
These are special methods of a class which contain initialization logic of fields Constuctor initializes fields and also contains the additional logic.
Employee emp1 = new Employee(101, "Scott", "Manager");
//fields
public int empID;
public string empName;
public string job;
//constructor
public Employee(int empID, string empName, string job)
{
this.empID = empID;
this.empName = empName;
this.job = job;
}
- Constructor name should be same as classname.
- Is private by default
- Can and cannot have params
- static is the only available modifier to a constructor
- recommended to be public or internal
- If it is a private it can only be called within the same class.
- Can have one or more params
- Cannot return a value; no return type. Is treated as void
- A single class can have on or more constructors. But all constrcutors mush have different params
- Can contain any init logic to be executed everytime
Created using static modifier
- Initializes static fields
- Executes only once ie when first object is created for the class or when the class is accessed for the first time in main method
- Is public by default
- access modifier cannot be changed
- Can contain init logic htat should be executed only once when a new object is created
- When we access the class for the first time the static constructor is initialized automatically
Employee emp1
System.Console.WriteLine("Company Name: ");
System.Console.WriteLine(Employee.companyName);
public static string companyName;
static Employee(){
companyName = "Joey Industries" // note were missing this
}
Provided by compiler or developer
There are two types of constructor: Parameterized and Parameter less (fixed values)
Implicit (after compilation):
- Automatically provided constructor on compiliation. Which inits nothing. Also called the default constructor
- done only to satisfy the rule that class should always have constructor
Explicit (while coding):
- Created manually by developer
- In the case that c# compiler downst provide a default constructor
Defining multiple constructors within same class
This is when we have mulitple constructors wiht same name in class with different parameters. (like method overloading)
- Its recommended to use parameter-less constructor in the class in the case of overloading
Employee emp1 = new Employee(); //num of params points to constructor 3
emp1.empID = 101; // this value will be 1
//constructor 1
public Employee(int empID, string empName, string job)
{
this.empID = empID;
this.empName = empName;
this.job = job;
}
//constructor 2
public Employee(int empID, string empName)
{
this.empID = empID;
this.empName = empName;
}
// constructor 3
public Employee() {
empID = 1 ;
}
Initialize field of an object without constructor.
Special syntax to initialize fields/properties of class along with creating object.
- executes after constructor
- it is only for init of fields/properties creating object(it cant have any init logic)
Exec Sequence: new Class() -> Constructor -> Object Initializer
Good if: You cannot find the matching constructor. And you want to initialize a specific bunch of fields
Eg: Employee emp4 = New Employee() {empName ="Bob", job ="Exectutive"}
- Instance constructor initializes instance fields but can also access static fields
- Static constructor inits only static fields cant access instance fields
- Empty constructors are provided by default
- Recommended to write your paramter-less constructor first (if you are creating a parameterized constructor later)
- Use object initializer if you want ot initialize desired fields of an object, as soon as new object created.
A property receives incoming value; validates the value; assigns value into a field. A property can be a collection of two accessors: get-accessor and set-accessor
Set Accessor: set {field = value}
- a value its important to Validate before initialization
- has a default (implicit) parameter called value assigned
- Cant have any additional params
- Cant return a value
Get Accessor: get {return field}
- You can calculate a value an return the same (or) return the value of the field as is.
- Executes automatically when the property is retrieved
- Has no implicit parameters
- Cant have parameters
- Should return any value
- Properties create a protection layer around fields. Also do some calculation automatically when someone has invoked the properyy
- no memory will be allocated for the property
- access mods is applicable for the property, set accessor & get accessor individually
- access modifiers if accessors must be more restrictive than the access modifier property
Private fields should be prefixed with an _
private int _empID
;
In console app we use intialization/assignment as usual
Employee emp1 = new Employee();
emp1.EmpID = 101;
Console.WriteLine(emp1.EmpID);// Outputs 101
public int EmpID{ // Note the use of PascaleCase on the property
set {
if(value >=100){ // with validation
_empID = value;
}
}
get { return _empID;}
}
When creating field instances it is good practice to create them as private (if not required to access directly outside of class)
When using the constructor remember we are not triggering the properties in this case. And the property validations will not be executed
Overall its best to create properties for all fields as this is the best place to validate them.
private static string _companyName;
public static string CompanyName{
set {
if(value.Length <=20){ // with validation
_companyName = value;
}
}
get { return _companyName;}
}
- are properties with only a get accessor
- reads and returns with no modifications
optionally you can initialize the value of the read only field into the constructor If you try and write to this property you will get a cannot be assigned - its read only error
- are properties with only a set accessor
- Validate and assign incoming value into field, but return the value
The only way to access this is to create a method to be able to return this value
- These are properties with no definition for set-accessor and get-accessor.
- Used to create property easily (with shorter syntax)
- Creates private field (with name as _propertyName) automatically at compilation time
- Auto-implemented property can be write only property or read-only
Good if: You dont want to write any validation or calculation logic
Employee.emp1 = new Employee();
emp1.NativePlace = "New Delhi";
//automatic property
public string NativePlace{
internal get; // with access modifier will only be accesible within class employee assembly
set;
}
You can mix auto implemented and manually implemented in your code there is no issue
Initializing default value into auto-implemented properties New feature of c# 6.0
//automatic property with auto implementiation
public string NativePlace{get;set;}= "New York"; //Has been auto-initialized but can still be overwritten should you wish
- Its recommended to always use
- you can use autoimplementation if it makes things easier
- Dont occupy any memory
- They provide a protection layer
- Can use readonly and writeonly
- Cant have additional parameters
Recieves a number / string. Search for the particular item among a group of items; set or get value into groups
It provides a shorter syntax to access a group of items.
It is a special member of class, which contains set-accessor and get-accessor to access a group of items / elements.
Car c = new Car();
Console.WriteLine(c[1]); // get a value
c[2] = "Jaguar"; //set or overwriting a value in indexer
Console.WriteLine(c[2]);
//private
private string[] _brands = new string[] { "BMW", "Skoda", "Honda" };
//public indexer
public string this[int index]
{
set
{
this._brands[index] = value;
}
get
{
return _brands[index];
}
}
πππ
- Indexers are always created with this keyword
- Are generally used to access a group of elements (items)
- Parameterized properties are called indexers
- Indexer must have one or more params
- ref and out parameter modifiers are not allowed
- Indexers cant be static
- Indexer is identified by its signature (where as property is its name)
- Indexer cant be static
- Indexer can be overloaded
This makes our indexer accessible via int and string parameters
//overloading
Console.WriteLine(c["first"]);
//indexer overloading
public string this[string name]
{
set
{
this._brands[Array.IndexOf(_names,name)] = value; // note we are retrieving from brands
}
get
{
return _brands[Array.IndexOf(_names,name)];
}
}
- Concept of extending the parent class by creating a child class
- Allows classes to be arranged in hierarchy is a type of relationship
- The parent class acts as a "base type" of one or mpre child classes child classes are derived from parents
Child class contains parent + childs members/fields
Syntax: class Manager:Employee
Single: ParentClass->ChildClass One Parent, One Child Class Multiple: ParentClass->ChildClass1->ChildClass2 Multiple Parent Classes, One Child Class
interface Teacher
{
}
interface Physician
{
}
class MedicalTeacher : Teacher, Physician // To have two parents like this we need to use interface
{
}
Multi-level: One Parent Class, One Child Class; and the Child class ahas another child class
Hierachical: One Parent, Multiple Child Classes
Hybrid: Hierachical Inheritance + Multilevel Inheritance
Accessing parent class's members in child class
- Optional
- base keyword represents parents clases members in the child class
- It is must to use when there is ambiguity
//method
public string GetFullDepartmentName()
{
return DepartmentName + " at " + base._location;
}
- accesible fields from parent class
- protected field of a parent class can only be accessible via the child class method
Calling the constructor of parent class from child class
- Parent constuctor is called first
- Child constructor only initializes its own constructor
- Its Optional to call parent classes parameter less constructor from child
- It is a Must to call Parents classes parameterized constructor from the child class and pass arguments
Employee emp1 = new Employee(101, "Scott", "Hyderabad");
Manager mgr1 = new Manager(102, "Allen", "New York", "Accounting");
public class Employee
{
//constructor of parent class
public Employee(int empId, string empName, string location)
{
this._empID = empId;
this._empName = empName;
this._location = location;
}
}
public class Manager : Employee{
//constructor of child class
public Manager(int empID, string empName, string location, string departmentName) : base(empID, empName, location)
{
_departmentName = departmentName;
}
}
- Note use of base() that inits the parent constructor. &
- we are just passing same parameters (mpID, empName, location) to the base constructor
- base constructor gets executed before the body of the child constructor
- dont initialize the parent class fields in the child class constructor
Hiding/overwriting parent class's method using new keyword Uses same name and parameters
- new keyword is optional but encourged
- Only done in child class
public class Employee
{
public GetHealthInsuranceAmount()
{
return "Amount is: 1500"
}
}
public class Manager : Employee{
//method hiding
public new GetHealthInsuranceAmount()
{
return "Amount is: 1500"
}
}
Using virtual keyword at parent class's method; override keyword in the child class's method
- Extending a parent classes method in the child class with the same name and params
- In child class you must use invoke parent method by using base keyword:
base.MethodName()
- When overriding is done, if the method is called using child class object; the parents class method first executed followed by the child
- without virtual keyword in the parent class method the child cannot be overwritten
This is differnt to hiding as BOTH methods will exectute
class Program
{
//Method overwriting
System.Console.WriteLine(mgr1.GetHealthInsuranceAmount()); //Employee.GetHealthInsuranceAmount and Manager.GetHealthInsuranceAmount
}
public class Employee
{
//virtual method
public virtual string GetHealthInsuranceAmount()
{
return "Health Insurance amount is: " + 500;
}
}
public class Manager : Employee{
//method overriding
public override string GetHealthInsuranceAmount()
{
System.Console.WriteLine(base.GetHealthInsuranceAmount());
return "Health Insurance amount is: " + 1000;
}
}
Usefull when you want to use the original method and in addtion supply your own. Also when working with framworks such as WPF where we extend parent classes.
A sealed class is a class that can be instantiated but not inherited
- Use this class whenever you dont want to let other developers to create child classes. Prevent Inheritance.
sealed public class Manager : Employee{
}
public class BranchManager : Manager{
// This will not work!
}
Sealed methods must be "override methods"; which can't be overidden in the corresponding child class. The oposite of virtual method. sealed override
- Good If: You want to prevent overriding that particular method in the corresponding child classes.
public class Manager : Employee {
public sealed override string GetHealthInsuranceAmount()
{
System.Console.WriteLine(base.GetHealthInsuranceAmount());
return "Additional Health Insurance premium amount is: 1000";
}
}
A parent class for which we can't create an object but can create child classes
What:
- Provides common set of fields and methods to all of its child classes of a group
- can contain all types of members
- Has to be initialized through child
Good if: You want to create a class for which you feel creating an object is not meaningfull
public abstract class Employee{
}
Abstract methods are declared in the parent class, with abstract keyword; implemented in child classes with override keyword
Good if: When you dont want the parent method to provide a definitiion of the method.
- Child class must implement body of the parent class
- can contain method declaration only but not method body
- child class must provide method body for abstract method
//parent class & abstract class
public abstract class Employee
{
//abstract method
public abstract string GetHealthInsuranceAmount(); //No method body
}
Use virtual method when the parent class method wants to provide the body. (It is optional to override in the child) Else use abstract.
Remember it is compulsary to override the absract methods
System.Console.WriteLine(salesMan1.GetHealthInsuranceAmount()); //SalesMan.GetHealthInsuranceAmount //Output 500
//parent class & abstract class
public abstract class Employee
{
//method overridding the abstract methods
public override string GetHealthInsuranceAmount(){
//Method body
}
}
//child class
public class SalesMan : Employee
{
//method overriding the abstract methods
public override string GetHealthInsuranceAmount() // Notice again all isthe same apart from the body
{
return "Additional Health Insurance premium amount is: 500";
}
}
In all abstract classes are as easy as:
- Create method in parent class without method body
- Create method body in child class with same name, parameters,same type and access modifiers
- Call the method through child class object
Think of an interface as set of just abstract methods, that must be implemented in child class. Defualt implementation
Example: interface IVehicle: (doesnt provide any method definition only method names, signatures and parameters)
-
Car - Bus (Share common methods)
-
Generally interface names start with a I
-
Default of all access modifeier in interface is public and abstract
-
Cannot change the access modifier of interface methods
-
When implementing interface the child promisises to use all methods of the parent class
-
The method bodys are allowed to be different in each child class
-
Cannot create object of interface
-
Can create reference variable
-
Can have mutliple interfaces in the same child class
Good if: You want to imply to another developer to follow a bunch of methods
//interface
public interface IEmployee
{
//abstract methods
string GetHealthInsuranceAmount();
//auto-properties
int EmpID { set; get; }
string EmpName { set; get; }
string Location { set; get; }
}
Good practice to rename the class and file
//child class
public class Manager: IEmployee
{
//field
private string _departmentName;
private int _empID;
private string _empName;
private string _location;
//properties
public int EmpID
{
set
{
if (value >= 1000 && value <= 2000)
{
_empID = value;
}
}
get
{
return _empID;
}
}
public string EmpName
{
set
{
_empName = value;
}
get
{
return _empName;
}
}
public string Location
{
set
{
_location = value;
}
get
{
return _location;
}
}
//method implementation of the interface methods
public string GetHealthInsuranceAmount()
{
return "Additional Health Insurance premium amount is: 1000";
}
}
Good if: When you have a mix of methods where you want to define a method body and not, use abstract classes.
Good if: Whenever the in the parent you only want to provide abstract methods but dont want to provide any non abstract methods or other fields or constructors..
Provides the ability for the developer to define different implements for the same method in the same class or different classes.
Polymorphism:
- Compile time (aka early binding / static)
- Run-Time (Late Binding)
// compile time - method overloading
public void Add(int a, int b);
public void Add(int a, int b, int c);
// runtime - method overloading
abstract class ParentClass{
public abstract void Add(int a, int b);
}
class ChildClass1: ParentClass{
public override void Add(int a, int b);
}
class ChildClass2: ParentClass{
public override void Add(int a, int b);
}
ParentClass c1;
c1 = new ChildClass1();
c1.Add(10,20);// calls ChildClass1.add
c1 = new ChildClass2(); // reference of previous object will be deleted
c1.Add(10,20);// calls ChildClass2.add
// runtime - with interfaces
interface InterfaceName{
void add(int a, int b);
}
class ChildClass1:InterfaceName{
public void Add(int a, int b);
}
class ChildClass2:InterfaceName{
public void Add(int a, int b);
}
InterfaceName c1;
c1 = new ChildClass1(); // c1 is interface reference variable
c1.Add(10,20);// calls ChildClass1.add
c1 = new ChildClass2(); // reference of previous object will be deleted
c1.Add(10,20);// calls ChildClass2.add
note the same method has different implementations (polymorphism) this allows for differetn developers to pick and choose different implementations at runtime
Creating child classes with multiple parent interfaces
In c# multiple inheritance is only possible using interfaces, that means a child class can have multiple parent interfaces
Keypoints:
- One child - Multiple Parent Classes / Parent Interfaces is called Multiple inheritance
- The child Must Implement all methods of all the interfaces, that its inherited from
// Multiple Inheritance
interface Interface1{
void Method1(param1,param2,..);
}
interface Interface2{
void Method2(param1,param2,..);
}
class ChildClass: Interface1,Interface2{
public void Method1(param1,param2,...){
// do something
}
public void Method2(param1,param2,...){
// do something
}
}
Interface1 c1 = new ChildClass();
c1.Method1(..); // calls ChildClass.Method1
Interface2 c2 = new ChildClass();
c2.Method2(..); // calls ChildClass.Method2
A project is a collection of files
For date the universally accepted format is (1990-07-15)
Creating two methods with same signature in two different interfaces and implementing both interfaces in the same child class.
-
Explicit Interface implementation is used to implement an interface method privatley; that means the interface method becomes as private member to the child class
-
If a child inherits from two or more interfaces and there is a duplicate method. Use Explicit Interface Implementation
-
You can use Explicit Interface Implementation to create private implementation of interface method, so you can create abstraction for those methods.
interface Interface1{
void Method1(param1,param2); // Same signature
}
interface Interface2{
void Method1(param1,param2); // Same signature
}
class ChildClass: Interface1, Interface2{
// To combine these in a child class we need to implement them explicitly using respective interface name
void Interface1.Method1(param1,param2){
// do something
}
void Interface2.Method1(param1,param2){
// do something
}
}
Interface1 c1 = new ChildClass();
c1.Method1(a,b); // calls interface1.Method1 at ChildClass
Interface2 c2 = new ChildClass();
c2.Method1(a,b); // calls interface2.Method1 at ChildClass
Namespaces are a collection of classes and other types such as interfaces, stuctures, delegate types and enumarations.
Main aim is to group these types as a single unit for a specific purpose. This helps for organization.
Although its best to have a namespace for each file. But can help for organization to reapply namespace grouping accross multiple.
We access the class via the namespace like so:
NamespaceName.Typename reference
FrontOffice.CustomerEnquiry customer
class Program
{
static void Main()
{
FrontOffice.CustomerEnquiry customerEnquiry = new FrontOffice.CustomerEnquiry();
}
}
//CustomerEnquiry.cs
namespace FrontOffice
{
//class that represents an enquiry by customer
public class CustomerEnquiry
{
}
}
//FrontOfficeExecutive.cs
namespace FrontOffice
{
//class that represents an executive to deal with customers at front office
public class FrontOfficeExecutive
{
}
}
Although we have differnt files these are the same namespace
A namespace declared inside another namespace. The aim is to create a subgroup for better organization
Its bad practice to have a class and namespace with the same name
To access this we need to say:
OuterNamespaceName.InnerNamespaceName.Classname reference
HR.Mgr.IEmployee manager
namespace HR
{
namespace Mgr
{
//interface that represents a manager, which inherits from IEmployee
public interface IManager : IEmployee
{
}
//class that represents an assistant manager, which inherits from IManager
public class AsstManager : IManager
{
}
}
//Other hr related types
}
You can use the using directive to import a namespace at the top of a file
This gets around having to repeat the long namespace name
- The using is a direective statement (top-level statement) that should be placed at the top of the file, which specifies the namespace from which you want to import everything.
using HR.Mgr;
class Program
{
static void Main()
{
IManager manager;
manager = new HR.Mgr.Manager();
}
}
The using alias directive allows you to create an alias name for the namespace. Shortnames (nicknames) for the long name part.
These aliases can only be used in one file
using m = HR.Mgr;
class Program
{
static void Main()
{
m.IManager manager;
manager = new HR.Mgr.Manager();
FrontOffice.CustomerEnquiry customerEnquiry = new FrontOffice.CustomerEnquiry();
}
}
Meaning you can access all the static methods and properties directly without accessing the classname.
using static System.Console; // system is a static class
class Program
{
static void Main(){
WriteLine();
}
}
A partial class is a class that spans accross multiple files. The way to do this is to define them as partial and wrap within a namespace
//file1
namespace Warehouse
{
public partial class Product{
}
}
//file2
namespace Warehouse
{
public partial class Product{
}
}
This can make it easier for a team of developers to work on the same class.
Partial Methods are declared in one partial class (just like abstract method) and implemented in another partial class, that have same name.
- Partial methods are private by default
- can only have void return type
- The can only be created in partial class or structs
//File1.cs
partial void GetTax(); // must have same signature
//File1.cs
partial void GetTax() // Notice body defined here
{
double tax = Cost * 10 / 100;
System.Console.WriteLine(tax);
}
Static class is class that can only contain static members
- Good if: you dont want even a single instance member.
- Avoids accidental creation of object for the class by making it a static class.
class Program
{
static void Main()
{
//access static fields
Console.WriteLine(Country.CountryName); // No object needed!
Console.WriteLine(Country.NoOfStates);
Console.WriteLine(Country.GetNoOfUnionTerritories());
Console.ReadKey();
}
}
public static class Country
{
public static string CountryName = "India";
public static int NoOfStates = 28;
public static int GetNoOfUnionTerritories()
{
return 8;
}
}
Enumeration is a collection of constants
Good If: you dont want to allow other developers to assign other value into a field/variable, other than list of values specified by enumeration.
Accessible like:
EnumerationName.ConstantName
;
class Program
{
static void Main()
{
person.AgeGroup = AgeGroupEnumeration.Adult;
Console.WriteLine(person.AgeGroup);
}
}
public enum AgeGroupEnumeration : short //explicit DataType
{
Child,
Teenager,
Adult = 100, //You can also explictly add a value.
Senior
}
Each enumaration value has a stored integer value.
Two data types:
Value types: (Structures, Enumerations)
- Storing simple values
- Instances are called structure instnaces or enumeration instances
- Stored in the stack
Reference Types: (string,Classes,Intefaces,Delegates)
- Storing complex data / large amounts
- Objects (Class Instances etc)
- Instances (objects) stored in the heap
A structure is a type similar to class which can contain fields, methods, parameterized connstructors, properties and events.
Good if: you want to store a limited amount of fields 1 or 2. They are a little bit faster to store in stack. (no need to access heap)
- Structs do not support inheritance
- Cannot create refrence or object
- Can implement Interfaces
- Do not support virtual and abstract method
- Do not support destructors
- Internally derived from System.ValueType class
public struct Category:System.ValueType
- Does not support init of "non static fields"
- Doesnt support protected and protected internal access modifier
- Do not support null values
class Program
{
static void Main()
{
//create structure instance
Category category = new Category();
//initialize fields through properties
category.CategoryID = 20;
category.CategoryName = "General";
//access methods
Console.WriteLine(category.CategoryID);
Console.WriteLine(category.CategoryName);
Console.WriteLine(category.GetCategoryNameLength());
Console.ReadKey();
}
}
public struct Category //accessible in the main method
{
//private field
private int _categoryID;
private string _categoryName;
//public fields
public int CategoryID
{
set
{
if (value >= 1 && value <= 100)
{
_categoryID = value;
}
}
get
{
return _categoryID;
}
}
public string CategoryName
{
set
{
if (value.Length <= 40)
{
_categoryName = value;
}
}
get
{
return _categoryName;
}
}
public int GetCategoryNameLength()
{
return this._categoryName.Length;
}
}
- C# provides a default parameter-less constructor for every structure by default which initializes all fields.
- You can also create one or more user defined constructors
- Cach parameterized constructor must initialize all fields otherwise it will create compile-time error
Category category = new Category(30,"New Name");
public Category(int categoryID, string categoryName)
{
_categoryID = categoryID;
_categoryName = categoryName;
}
Use readonly when:
-
fields are read only
-
all properties have get accessors
-
there is parameterized constructor
-
you dont want to allow any change
-
methods can read fields but not change anything
-
readonly is a new feature 8.0
-
feature improces the perfomance
class Program
{
static void Main()
{
// create structure instance
Marvel m = new Marvel("Thanos");
Console.WriteLine(m.CharacterName); //property
m.PrintCharacterName(); //method
}
}
public readonly struct Marvel
{
private readonly string _characterName;
public string CharacterName {
get { return _characterName; }
}
public void PrintCharacterName()
{
System.Console.WriteLine(this.CharacterName);
// this._characterName = "abx"; // You cant do this
}
public Marvel( string CharacterName)
{
this._characterName = CharacterName;
}
}
-All primitive types except string are structures
- For example sbyte is a primitive type which is equivalant to "System.Sbyte" structure
- Its recommended to always use primitive types instead of structure
//create a structured variable
sbyte a = 10; // equivalant to System.Sbyte
//Create a reference variable and object of string
string b = "Hello"; //System.String
// same as
System.String b = "Hello";
The System.Object is a pre-defined class, which is the Ultimate super (base class) in .net
class System.ValueType: (is parent to)
- Structures
- enumerations
All the classes and other types are inherited from this super class
The benefit of this is to maintain type safety
//System.Object Class [Pre-defined]
namespace System{
class Object{
virtual bool Equals(object value); // returms true of both are equal but this can be overwritten
virtual int GetHashCode(); // numerical value to check whether two object are the same or not calling on each object and then comparing good for collections or hash sets
Type GetType(); //Not a virtual method so no override, this will return the typename (classs,interfaces)
virtual string ToString(); //Can be overridden
}
}
//Overiding System.Object class
class Program
{
static void Main()
{
//create an object of Person class
System.Object obj = new Person() { PersonName = "Scott", Email = "scott@gmail.com" };
//access methods
Console.WriteLine(obj.Equals(new Person() { PersonName = "Scott", Email = "scott@gmail.com" }));
Console.WriteLine(obj.GetHashCode());
Console.WriteLine(obj.ToString());
Console.WriteLine(obj.GetType().ToString());
Console.ReadKey();
}
}
As per above
- Conversion from Value-Type to Reference-Type
- Boxing is done automatically
- Copies reference and primtives from the stack to heap
//primitive variable
int x = 10;
//boxing (value-type to reference-type)
object obj = x;
Conversion from Reference-Type to Value-Type
//reference type variable
object obj = 10;
//unboxing (reference-type to value-type)
int x = (int)obj;
Generic class is a class, which contains one or more "type parameters"
-Can store various types of fields with respect to various types of object
- You must pass any data type (standard data / structure / class ) while creating object for the generic class.
Good if: you want the object to to decide the datatype. And you not sure of what datatype you are going to pass.
-
You must define your datatype later when instantiating an object
-
use generics when your not sure of the data types of the specific fields
//Generic class
public class User<T>{ //DataType T (Just a generic name )
//generic field
public T RegistrationStatus; //must only store this data type in field
}
//Object of Generic Class - Example
//Create object of generic class
User<int> user1 = new User<int>();
User<bool> user2 = new User<bool>(); //Changing of type
user1.RegistrationStatus = 123;
user2.RegistrationStatus = false;
Console.WriteLine(user1.RegistrationStatus);
Console.WriteLine(user2.RegistrationStatus);
We can further specify constraints at object instantiation
//create object of generic class
User<int, int> user1 = new User<int, int>(); // first and second types must be of int
User<bool, string> user2 = new User<bool, string>(); // first bool second string
//set value into generic field
user1.RegistrationStatus = 1234;
user2.RegistrationStatus = false;
user1.Age = 22;
user2.Age = "35 - 40";
//Generic class
public class User<T1, T2> // Specify we have two types
{
//generic field
public T1 RegistrationStatus;
//another generic field
public T2 Age;
}
Are the expectations or restrictions on the paramters. Generic constraints are used to specify the allowed types to be accepted in the generic type parameter
Abstract means you do not want to create an object of this class directly
//create object of generic class
MarksPrinter<GraduateStudent> mp = new MarksPrinter<GraduateStudent>(); // Must be student or a child class of
mp.stu = new GraduateStudent() { Marks = 80 };
mp.PrintMarks();
//generic class with constraints (want to accept Student or its child classes only)
public class MarksPrinter<T> where T : Student // Here we could use class simply or be more specific make a specific class
{
public T stu;
public void PrintMarks()
{
Student temp = (Student)stu; // Typecast class into field
System.Console.WriteLine(temp.Marks);
}
}
You could contingue on to specify more constraints like so
public class MarksPrinter<T1, T2> where T : Student where T2: employee
Generic method is a method that has one or more generic parameters.
You can restrict what type of data types (class names) allowed to be passed when creating the object
Good if: you are not sure what type of value you want to revieve.
Suppose we have two classes employee and student with some methods. We can define a generic class that accepts these external methods.
//Program.cs
static void Main()
{
Sample sample = new Sample();
Employee emp = new Employee() { Salary = 1000 };
Student stu = new Student() { Marks = 10 };
//sample.PrintData<Student>;
sample.PrintData<Student>(stu); // this gets passed into obj
}
//Clas1.cs
public class Employee
{
public int Salary;
}
public class Student
{
public int Marks;
}
//a class with generic method
public class Sample
{
//generic method
public void PrintData<T>(T obj) where T : class
{
if (obj.GetType() == typeof(Student)) // checks to see if method supplied is of student
{
Student temp = obj as Student;
System.Console.WriteLine(temp.Marks); // we can then access the marks property
}
else if (obj.GetType() == typeof(Employee))
{
Employee temp = obj as Employee;
System.Console.WriteLine(temp.Salary); // we can then access the salary property
}
}
There are two types:
Value types: (structures, enumerations)
- Are by default non-nullable
- Non nullable tpyes dont support null values
Nullable <int> x = null
int? x = null
namespace namespace1
{
class Person
{
public int? NoOfChildren; // this structure is now nullable
}
class Program
{
static void Main()
{
//create object
Person p1 = new Person() { NoOfChildren = null };
}
}
}
References types: (classes, interfaces)
- by default nullable types
- support null values
- dont require the below syntax
Sometimes in the realworld if this value is not equal to null you will need to access the value of a nullable type, then its required to use
value
. And check usingHasValue
if (p2.NoOfChildren.HasValue)
{
int x = p2.NoOfChildren.Value;
Console.WriteLine(x);
}
Short hand to check if the value is null or not
- The 'null coalescing operator' checks whether the value is null or not.
- It returns the left-hand-side operand if the value is not null.
- It returns the right-hand-side operand if the value is null.
// variableName ?? valueIfNull
Console.WriteLine(p2.NoOfChildren ?? 0); //if value is not equal to null set to 0
When using Null Coalescence the datatype of alternative should be the same as the actual field
Used for reference variables..
-The "Null Propagation Operator ( ?. ) and ( ? [] ) checks the value of left-hand operand whether it is null or not. -It returns the right-hand-side operand (property or method), if the value is not null. -It returns null, if the value is null. -It accesses the property or method, only if the reference variable is "not null"; just returns "null", if the reference variable is "null".
-We can invoke desired member (property or method) after checking if null.
referenceVariable?.fieldName;
-- is same as --
(referenceVariable == null)? null : referenceVariable.fieldName;
class Person
{
public int Age;
}
class Program
{
static void Main()
{
//p1 is null
Person p1 = null;
//print age
Console.WriteLine( p1?.Age );//checks the value of left returns the right if not null then trys to access its field property or method
Console.ReadKey();
}
}
Avoids Null reference Exception
Method is injected into existing class without modifying source code of that class GetDiscount(this Product product)
(see pdf)[_resources\Extension+Methods+Cheatsheet.pdf]
-
Must be a static class only with static method. This will get specified as instance method via this.
-
cannot use this inside static method
-
Dont forget to import the namespace for which the extension class is different
Product p = new Product() { ProductCost = 1000, DiscountPercentage = 10 };
//call the extension method
Console.WriteLine(p.GetDiscount());
namespace ExtensionsNamespace
{
//static class for extension method
public static class ProductExtensions
{
//extension method for Product class
public static double GetDiscount(this Product product) // instance method via *this*.
{
return product.ProductCost * product.DiscountPercentage / 100;
}
}
}
Allows you to decalare a vairable while checking the data type of reference and automatically type casts the ref variable into a specified data type (class) (see pdf)[_resources\Pattern+Matching+Cheatsheet.pdf]
object greeting = "Hello, World!";
if (greeting is string message) //
{
Console.WriteLine(message.ToLower()); // output: hello, world!
}
(see pdf)[_resources\Implicitly+Typed+Variables+Cheetsheet.pdf]
//implicit assignment
var variableName = value;
// Implicit type usage
// Using var keyword to shorten a legnthy variable
var p = new namespace1.namespace2.namespace3.Person() { PersonName = "Harsha" };
//calling a method on written type
var p2 = "Harsha".ToUpper(); //Datatype is implicit as string
// You cant do this it must be intialized
//var p3;
- You cant decalre multple
- not possbile either to assign to null
Variables that are declare with dynamic keyword and can potentially change data type
(see pdf)[_resources\Dynamically+Typed+Variables+Cheatsheet.pdf]
Accepts any datatype.
Good if: your not sure what datatype you want to store use dynamic
- C# will not type check this
//dynamically typed variable
dynamic x;
x = 100;
x = "Hello";
x = new Student() { StudentName = "Harsha" };
Console.WriteLine(x);
We can create all inter-related classes of a class, "inner-classes" (see pdf)[_resources\Inner+Classes+Cheatsheet.pdf]
Syntax: outerclass.innerclass
class ClassName
{
class InnerClassName
{
Members here
}
}
- By default, inner class is "private"
- Outer class needs object to access inner class
- You cant do both
//accessing an inner class from the class
//outer class
public class MarksCalculation
{
public void CalculatePercentage(Student s)
{
//create object of inner class
CalculationHelper ch = new CalculationHelper();
s.Percentage = ch.Multiply( s.SecuredMarks / s.MaxMarks, 100);
}
//inner class
public class CalculationHelper
{
public double Multiply(double n1, double n2)
{
return n1 * n2;
}
}
}
Garbage Collection is a process of deleting
objects from memory, to free-up memory;
so the same memory can be re-used. Its envoked autmatically when freespace is required
Optionally can call GC.Collect()
explicitly
(see pdf)[_resources\Garbage+Collection+Cheatsheet.pdf]
Destructor is a special method of the class, which is used to close un-managed resources (such as database connections and file connections), that are opened during the class execution. This happens automatically.
There are two tpyes of resources in c#: Managed: Objects created by Common Language Runtime Participate in garbage collection
Unmanaged: Objects not created by Common Language Runtime Eg: File Streams & DB Connections
Good if: When we want to close db connections and file connections to avoid leakage.
Usually called at the end of a class
(see pdf)[_resources\Destructors+Cheatsheet.pdf]
public class Sample
{
//constructor
public Sample()
{
//file & db connection logic here
Console.WriteLine("File is opened");
}
//destructor
~Sample()
{
//file & db closing logic here
Console.WriteLine("File is closed");
}
}
Is a predefined interface of System namespace, has a method called dispose which is used to close unmanaged resources that are created during the life-time of the object.
- can be used as alternative to destructor
- suppose you have a project running for many hours this allows developers to jump in and free up memory
- With dispose method you need not wait till the end of running the application
- Particularly in real world projects within business access layer /database layer you will find dispose
To move sequentially thought the program you can use the F10 Command
- Use the using only in for the class that contains the dispose method with Databse method is placed
(see pdf)[_resources\IDisposable+Cheatsheet.pdf]
/*
Output
Database connected.
Reading data from database
Database disconnected
Some other work here
*/
namespace IDisposableExample
{
class Program
{
static void Main()
{
//create object using "using structure"
using (Sample s = new Sample())
{
//This will jump from the constructor, to any specified method like below and finally to the dispose method
s.DisplayDataFromDatabase();
}
Console.WriteLine("Some other work here");
Console.ReadKey();
}
}
}
namespace ClassLibrary1
{
public class Sample : System.IDisposable
{
//constructor
public Sample()
{
Console.WriteLine("Database connected.");
}
//method
public void DisplayDataFromDatabase()
{
Console.WriteLine("Reading data from database");
}
//Dispose
public void Dispose() //called automatically no need to wait untill main
{
Console.WriteLine("Database disconnected");
}
}
}
3 steps to this process:
- Implement the called
public class Sample : System.IDisposable
- Implement the Dispose method
- Required to use using stucture and at compilation time this will close via dispose mehtod
- You can prefix "using" keyword before the local variable declaration,in order to call "Dispose" method when that variable goes out of scope.
This the same as prebious example but rather than putting it in a () brackets were are using a declarations. To specify methods you can simply instantiate them after creating reference variable. Automatically Dispose()
will be called.
using Sample s = new Sample();
s.DisplayDataFromDatabase(); //instantiate them after creating reference variable.
This feature is only available in c# 8.0. However you can change this value manually. Right click on the project solution and open in file explorer. Use something else than visual studio for this. Find the file ext .csproj and form here you can modify the language version 8.0. You may need to reload afterwards.
F11: will take you to every line including your function body, but F10 allows to move from one line to the the immediate next line
If you dont use using dispose
will have to be declared explicitly.
Delegate type is a type that represents methods that have specific parameters and return type.
The delegate object is an object that stores a reference (address) of a specific method of a specific class with compatibile parameters and return type
(see pdf)[_resources\Delegates+Cheatsheet.pdf]
- Helpfull for building events
- They are the building blocks of events
- References one or more methods System.Delegate
- Each delegate will have a specific name
- In here you can store a reference to one or more methods
- The delegate type is more like a condition
- Single-cast delegate object can have a return type
This is like queing up (delegating responsibility) methods from a class (sample) injecting them into a delegate type then invoke
ing them passing paramters.
using System;
using ClassLibrary1;
namespace SingleCastDelegatesExample
{
class Program
{
static void Main()
{
//create object of Sample class
Sample s = new Sample();
//create delegate object (or) delegate
MyDelegateType myDelegate;
//add/pass method reference to delegate
myDelegate = new MyDelegateType(s.Add);
//invoke method using delegate object
Console.WriteLine(myDelegate.Invoke(30, 40)); //Here we need to match the number of parameters
Console.ReadKey();
}
}
}
using System;
namespace ClassLibrary1
{
public class Sample
{
//target method
public int Add(int a, int b)
{
int c = a + b;
return c;
}
}
}
using System;
namespace ClassLibrary1
{
//create delegate type
public delegate int MyDelegateType(int a, int b);
}
Notice that both the delegate and sample class are within the same namespace.
When you invoke multiple delegates. These can be called after passing the refs of a bunch of target methods then using myDelegate.Invoke(40, 10);
-
As its multicast delegate we will not be returning anything and we can set our delegate type to be
void
-
In our sample class we also now have multiple methods
using System;
using ClassLibrary1;
namespace MultiCastDelegatesExample
{
class Program
{
static void Main()
{
//create object of Sample
Sample s = new Sample();
//create reference variable of MyDelegateType
MyDelegateType myDelegate;
//add ref of first target method
myDelegate = s.Add;
//add ref of second target method
myDelegate += s.Multiply;
//invoke both target methods; first Add method; and then Multiply method
myDelegate.Invoke(40, 10);
Console.ReadKey();
}
}
}
using System;
namespace ClassLibrary1
{
public class Sample
{
//target method 1
public void Add(double a, double b)
{
double c = a + b;
Console.WriteLine("Addition is: " + c);
}
//target method 2
public void Multiply(double a, double b)
{
double c = a * b;
Console.WriteLine("Multiplication is: " + c);
}
}
}
using System;
namespace ClassLibrary1
{
//delegate type
public delegate void MyDelegateType(double a, double b);
}
Rules:
- paramters should be same
- no return value
We really only use delegates in conjunction with events. Hardley ever directly.
Event is basically a delegate. A multicast delegate that stores one or more methods and invokes them when the event is raised.
They can only be raised in the same class in which they are created.
(see pdf)[_resources\Events+Cheatsheet.pdf]
Events are achieved through interactions between a publisher class and a subscriber class (Publisher informs subscriber)
Think of this like a bank account class with an event that gets the interest rate. When the interest rate is changed (publisher class) it informs the bank account calculate interest method to perfom an up to date calculation.
Publisher class:
- Sends (raises events)
Subscriber class:
-
Recieves (or subscribes or handles) events
-
we can only raise the event from the class of witch it has been created
Total Of 4 steps:
- Publisher class has event
- Subscriber class has to subscribe to event
- Publisher class has to raise event
- Then corresponding eventh andler method (add) must be executed in subscriber class
- this is also called the notification or publisher/subscriber pattern
- events are also found in button / form class. Button raises event. Form subscribes to event.
//Program.cs
using System;
using ClassLibrary;
namespace EventsExample
{
class Program
{
static void Main()
{
//Create object of subscriber
Subscriber subscriber = new Subscriber();
//Create object of publisher
Publisher publisher = new Publisher();
//handle or subscribe to the event
publisher.myEvent += subscriber.Add; // store/subscrive reference of add method in myevent
//invoke the event
publisher.RaiseEvent(13, 12);
Console.WriteLine();
}
}
//Publisher.cs
using System;
namespace ClassLibrary
{
//delegate type
public delegate void MyDelegateType(int a, int b); // this represents the parameters and delegate type of event
//publisher
public class Publisher
{
//private delegate
private MyDelegateType? myDelegate; //this will store one or more methods
//step 1: create event
public event MyDelegateType myEvent //allowing external to subscribe to event
{
add
{
myDelegate += value; //reference will be added to reference mydelgate above
}
remove
{
myDelegate -= value;
}
}
public void RaiseEvent(int a, int b) //raise event in same class only
{
//step 2: raise event
if (this.myDelegate != null) //if nobody has subscribed to the event its default is null
{
this.myDelegate(a, b); // 2 parameters in delegate type
}
}
}
}
//Subscriber.cs
using System;
using ClassLibrary;
namespace EventsExample
{
class Subscriber //The aim of this class is only to listen in for when publisher raises event
{
// here we can assign the reference of one or more methods to the event
//Target method
public void Add(int a, int b)
{
Console.WriteLine(a + b);
}
}
}
Creating event with auto-generated add-accesor and remove accessor Shortcutt syntax in order to create event Good: In the case, you need not create "add", "remove" accessors, the compiler does this automatically. just like Auto-Implemented property initializers
using System;
namespace ClassLibrary1
{
//delegate type
public delegate void MyDelegateType(int a, int b);
//publisher
public class Publisher
{
//c# compiler automatically creates private delegate field
//step 1: create event
public event MyDelegateType myEvent;
//c# compiler automatically create the code for add and remove accessors
public void RaiseEvent(int a, int b)
{
//step 2: raise event
if (this.myEvent != null) // then just call my event itself rather than the private delegate
{
this.myEvent(a, b);
}
}
}
}
The Add method in subscriber is also known as the event handler
- Disadvantage of this is we cant define custom logic for our add access & remove accessor
These are anonymous methods name-less methods that can be invoked by using the delegate variable or an event.
Good if: you want to create a method with very few lines of code π New feature of c# 2.0 (π see pdf)[_resources\Anonymous+Methods+Cheatsheet.pdf]
public delegate void Print(int value);
static void Main(string[] args)
{
Print print = delegate(int val) {
Console.WriteLine("Inside Anonymous method. Value: {0}", val);
};
print(100);
}
You use a lambda expression to create an anonymous function. Use the lambda declaration operator => to separate the lambda's parameter list from its body. A lambda expression can be of any of the following two forms: Expression: (input-parameters) => expression Statement: (input-parameters) => { sequence-of-statements> }
Func<int, int> square = x => x * x; //lambda expressions
Console.WriteLine(square(5));
Action<string> greet = name => //lambda statement
{
string greeting = $"Hello {name}!";
Console.WriteLine(greeting);
};
greet("World");
- Similar to anonymous methods
- Similar to delegate instead we use '=>'
Delegate with parameters and return Func is a pre-defined generic delegate which can be used to create events quickly
- 0 -16 parameters
- must have predefined value
(π see pdf)[_resources\Func+Cheatsheet.pdf]
public delegate TResult Func<in T,out TResult>(T arg);
// Func Syntax: public delegate TResult Func<in T, out TResult>(T arg);
Func<int, int> plusone = number => number + 1;
Console.WriteLine(plusone(3));
Predefined delegate in c# (mostly same as func without a return type) (π see pdf)[_resources\Action+Cheatsheet.pdf]
class Program
{
static void Main()
{
Action doWorkAction = new Action(DoWork); //by default is void
doWorkAction(); //Print "Hi, I am doing work."
}
public static void DoWork()
{
Console.WriteLine("Hi, I am doing work.");
}
}
Delegate with one parameter and boolean return (π see pdf)[_resources\Predicate.pdf]
class Program
{
static bool IsUpperCase(string str)
{
return str.Equals(str.ToUpper());
}
static bool IsPositiveNumber(int num)
{
if(num < 0) { return false; } else { return true; }
}
static void Main()
{
Predicate<string> isUpper = IsUpperCase;
bool result = isUpper("hi");
Console.WriteLine(result);
Predicate<int> isPos = IsPositiveNumber;
bool result2 = isPos(-1);
Console.WriteLine(result2);
}
}
Delegate for data exchange in events. Predefined delegate type
//Step 1. Declare a delegate with the signature of the encapsulated method
public delegate void MyDelegate(string input);
//Step 2. Define methods that match with the signature of delegate declaration
class Class1 {
public void delegateMethod1(string input)
{
Console.WriteLine("Hi im delegate method 1 with input "+ input);
}
public void delegateMethod2(string input)
{
Console.WriteLine("Hi im delegate method 2 with input " + input);
}
}
//Step 3. Create delegate object and plug in the methods
class Class2
{
public MyDelegate createDelegate()
{
Class1 c2 = new Class1();
MyDelegate d1 = new MyDelegate(c2.delegateMethod1);
MyDelegate d2 = new MyDelegate(c2.delegateMethod2);
MyDelegate d3 = d1 + d2;
return d3;
}
}
//Step 4. Call the encapsulated methods through the delegate
class Class3
{
public void callDelegate(MyDelegate d, string input)
{
d(input);
}
}
class Driver
{
static void Main()
{
Class2 c3 = new Class2();
MyDelegate d = c3.createDelegate();
Class3 c4 = new Class3();
c4.callDelegate(d, " Calling the delegate");
}
}
Introduced in c# 3.0
- Expression Tree is a collection of delegates represented in tree-like structure.
- Expression Tree only executes when we compile and execute it.
- Expression Trees support all delegate types such as Func, Action, Predicate or custom delegate types.
- Used always internally used in the concept of Linq (π see pdf)[_resources\Expression+Trees+Cheatsheet.pdf]
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace ExpressionTreesExample
{
class Student
{
public int StudentID { get; set; }
public string StudentName { get; set; }
public int Age { get; set; }
}
class Program
{
static void Main()
{
//Create object of Student class
Student s = new Student() { StudentID = 101, StudentName = "Scott", Age = 15 };
//Create expression tree with Func
Expression<Func<Student, bool>> expression = st => st.Age > 12 && st.Age < 20;
//Compile expression using Compile method to invoke it as Delegate
Func<Student, bool> myDelegate = expression.Compile();
//Execute the method
bool result = myDelegate.Invoke(s);
Console.WriteLine(result);
Console.ReadKey();
}
}
}
Expression Bodied Members concept allows the developer to use "Inline Lambda Expressions" to create methods, property accessors, constructors, destructors, indexers in a class
- provide a minimal and concise syntax to define properties and methods.
- May or may not have parameters
- May or maynot have return value
its convenient only if you have some simple code to convert to an expression
(π see pdf)[_resources\Expression+Bodied+Members+Cheatsheet.pdf]
//In its simplest form
public class Employee
{
public void DisplayName(string name) => Console.WriteLine(name); //Expression bodied member note use of "=>"
}
class Program
{
static void Main()
{
Employee employee = new Employee();
employee.DisplayName("Joey");
}
public class Employee
{
private string FirstName;
private string LastName;
public Employee(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public string GetFullName() => $"{FirstName} {LastName}";
public override string ToString() => $"{FirstName} {LastName}";
public void DisplayName() => Console.WriteLine(GetFullName());
}
class Program
{
static void Main()
{
Employee employee = new Employee("Pranaya", "Rout");
employee.DisplayName();
Console.WriteLine(employee);
Console.WriteLine("Press any key to exists");
Console.ReadKey();
}
}
Switch Expression is a short-form of "switch-case", which is used to check the value of source variable; assign value into result value based on the value of source variable.
(π see pdf)[_resources\Switch+Expression+Cheatsheet.pdf]
var operation = 2;
var result = operation switch
{
1 => "Case 1",
2 => "Case 2",
3 => "Case 3",
4 => "Case 4",
};
Console.WriteLine(result);
The variable used in switch expression is now coming before the switch keyword.
- Colon (:) and case keyword are replaced with arrows (=>). Which makes the code more compact and readable.
- The default case is now replaced with a discard(_).
- And the body of the switch is expression, not a statement.
- Switch Expression returns the result value based on the matching case.
- It is only meant for getting a specific result value; doesn't let you to write multiple statements.
Array is a group of multiple values of same type. Arrays are stored in continuous-memorylocations in 'heap'
(π see pdf)[_resources\Arrays+Cheatsheet.pdf]
Each value of array is called "element". βΊ All the elements are stored in continuous memory locations. βΊ The address of first element of the array will be stored in the "array reference variable". βΊ The "Length" property stores count of elements of array. Index starts from 0 (zero). βΊ Arrays are treated as objects of "System.Array" class; so arrays are stored in heap; its address (first element's address) is stored in reference variable at stack.
Iterating though array index based on a loop
//Iterating arrays with for
int[] b = new int[5] {10,20,30,40,50};
//Iterating arrays
for(int i = 0; i < a.Length; i++) //always use lefnth
{
Console.WriteLine(b[i]);
}
Contains an iteration variable works for arrays or collections loops through all elemts
For loop: Good if : You can read any part of the array However syntax is more complex
Foreach: Good if: you want to read values start to end
int[] b = new int[5] {10,20,30,40,50};
//Iterating arrays with foreach
foreach (int i in b)
{
Console.WriteLine(i);
}
Console.WriteLine();
Console.WriteLine("Printing for in reverse:");
//Iterating arrays with for in reverse
//Iterating arrays with for
for (int i = b.Length - 1; i >= 0; i--) // Starting number is Legnth -1; i >= is greater than or equal to 0, descend decrement
{
Console.WriteLine(b[i]);
}
Console.WriteLine("Printing for in reverse using Reverse():");
Every array is treated as an object of System.Array class. You do not need to specify this. The System.Array class provides a set of properties and methods for each array. Properties represent a value.
Properties:
- Legnth
Methods:
- IndexOf
- BinarySearch
- Clear
- Resize
- Sort
- Reverse
- CopyTo
- Clone
Used to search the array for a value (static method)
- if found returns index
- if not returns -1
- Always performs linear search from startIndex when found a value it stops searching
Syntax: (string value, int startIndex, int count, StringComparison comparisonType)
// IndexOf
double[] d = new double[6] {10,20,30,40,50,30}; // with initialization
Console.WriteLine(Array.IndexOf(d,30)); // this will return first value found
Console.WriteLine(Array.IndexOf(d, 30,3)); // this will return first value found from 3rd key
Console.WriteLine(Array.IndexOf(d, 100)); // returns -1
Better for large searches. It is faster
- if found returns index
- if not returns -1
- Binary search requires a sorted array
- goes directly to the middle of array and check item is less than/greater than search value
- If the item is greater than search value it searches only in the first half
- if the item is less than the search value it searches only the second half
- thus it only searches half the array
Syntax: public static int BinarySearch (Array array, object? value);
// Binary Search
double[] e = new double[]{ 10, 20, 30, 40, 50, 60, 70, 80, 100};
Console.WriteLine("30 is found in first half with index : " +Array.BinarySearch(e, 30)); //searhes only first half of array
Console.WriteLine("70 is found in second half with index: " +Array.BinarySearch(e, 70)); // this will return first value found
Clears the contents of an array. Sets 0 for all specified elements. For string type the values will be set to null
public static void Clear (Array array, int index, int length);
//public static void Clear (Array array, int index, int length);
int[] f = new int[] {0, 10, 20, 30, 40, 50, 60, 70, 80, 100 };
//Array.Clear(f, 0, f.Length); clear all
Array.Clear(f, 0, f.Length / 2 ); //clear half
Console.WriteLine(f);
foreach(var item in f)
{
Console.WriteLine(item);
}
This method increases / decreases size of the array
Syntax: public static void Resize<T> (ref T[]? array, int newSize);
//Array resize
Console.WriteLine("Resizing Array ArrayResize()");
string[] g = {"The", "quick", "brown", "fox", "jumps","over", "the", "lazy", "dog"};
Array.Resize(ref g, g.Length - 3); // Shorten array by 3
foreach (var item in g)
{
Console.WriteLine(item);
}
int[] h = new int[] { 0, 10, 20, 30, 40 };
Array.Resize(ref h, h.Length +5 ); // Shorten array by 3
foreach (var item in h)
{
Console.WriteLine(item);
}
Sorts array.
The default is to sort ascending order
Syntax (simple): static void Array.Sort(System.Array array)
;
- Works easily with numbers & strings
// Array sort
string[] j = new string[] { "Mahesh", "David", "Allen", "Joe", "Monica" };
Array.Sort(j); // sort alhpabetical
foreach (string item in j)
{
Console.WriteLine(item + " ");
}
Reversing arrays
Syntax: public static void Reverse (Array array, int index, int length);
// Or
int[] b = new int[5] {10,20,30,40,50};
Array.Sort(b); // you can sort keys first
Array.Reverse(b);
for (int i = 0; i < b.Length; i++) //always use lefnth
{
Console.WriteLine(b[i]);
}
Array.Reverse(b);
^ This operator returns the endex from end of the array (last element treated as 0)
This new hat/cap symbol ^ operator is called the index from end operator: ^ . By adding a ^ before your array index value, C# will start at the end of the array and count backward to locate an element. ^1 refers to the last element in the array.
This is only available in c# 8.0
// Array Index From End Operator
Console.WriteLine("Index From End Operators ^");
int[] k = new int[] { 0, 10, 20, 30, 40 };
//index from end operator
Console.WriteLine(k[^1]); // gets last element
Console.WriteLine(k[^2]); // gets second to last element
//range operator ..
Console.WriteLine("Range [2..] From End Operators ");
foreach (int item in k[2..5])
{
Console.WriteLine(item);
}
Single dim: array is a group of multiple rows each a single value
Multi dim: each row contains a group of values (array of arrays). All values in array must have same amount of values.
Syntax: type[,] arrayReferenceVariable = new type[rowSize,columnSize];
//Multi dimensional arrays
// Two-dimensional array.
int[,] array2D = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } }; // each , represents a dimension
// The same array with dimensions specified.
int[,] array2Da = new int[4, 2] { //[rows,columns]
{ 1, 2 },
{ 3, 4 },
{ 5, 6 },
{ 7, 8 }
};
// A similar array with string elements.
string[,] array2Db = new string[3, 2] { { "one", "two" }, { "three", "four" },
{ "five", "six" } };
System.Console.WriteLine("2d array element value: "+ array2D[1, 0]);
Array of arrays. The number of columns can be different. They can be any size If you have different number of columns for each row then it is best to use a jagged array.
Syntax:
//jagged arrays Console.WriteLine("Jagged Arrays");
int[][] l = new int[3][];
l[0] = new int[3]{1,2,3};
l[1] = new int[4]{4,5,6,7};
l[2] = new int[2]{1,2};
for(int i = 0; i < l.Length; i++)
{
//Console.WriteLine(l[i]);
for(int i2 = 0; i2 < l[i].Length; i2++)
{
Console.Write(l[i][i2]+ " ");
}
Console.WriteLine(" ");
}
We store the reference of the object in the array Stored in the heap Each element will still have its index
//Array of objects
//Create objects
Employee emp1 = new Employee() { EmpID= 1,EmpName = "Scott" };
Employee emp2 = new Employee() { EmpID = 2, EmpName = "Bill" };
Employee emp3 = new Employee() { EmpID = 3, EmpName = "James" };
//array of objects
Employee[] employees = new Employee[] { emp1, emp2, emp3 }; // you can only store those of type Employee
foreach(Employee item in employees)
{
Console.WriteLine(item.EmpName);
}
There are two predefined methods
CopyTo:
destination = source.CopyTo(destination,0);
public void CopyTo(int sourceIndex, char[] destination,int destinationIndex, int count)
- expects array to already exist destination array should be large enough to hold all the elements, starting from startindex
- You can specify start index
- You do not need to type cast Shallow copy: is when we copy the just reference from source to destination
Deep copy: creating new object and copying all values from one object to a new object
Clone:
destination = source.Clone();
- This creates a new destination array you need not have an existing one. Clone method* creates a new array object*
- doesnt allow you to specify start-index
- object that needs to be typecast first
//CopyTo
Employee[] employees2 = new Employee[4] {
new Employee(){ EmpID = 105, EmpName = "Hamid", EmpRole= "Teamlead", Location= "France"},
new Employee(){ EmpID = 104, EmpName = "Luis", EmpRole= "Teamlead", Location= "Madrid"},
new Employee(){ EmpID = 106, EmpName = "Joe", EmpRole= "Developer",Location= "Malaga"},
new Employee(){ EmpID = 107, EmpName = "Jacobo",EmpRole= "Developer",Location= "Vigo"}
};
// new array
Console.WriteLine("Copy to: \n");
Employee[]spainEmployees = new Employee[6]; // this index must match when copying too
employees2[0].Location = "Venice"; //shallow copy changes made in references as object is NOT copied
employees2.CopyTo(spainEmployees,2); // simple copy reference object = array, start index, also expects array to already exist
foreach (Employee emp in spainEmployees)
{
if(!(emp is null))
{
Console.WriteLine(emp.Location);
} else
{
Console.WriteLine("null object");
}
}
//Clone
Console.WriteLine("Clone: \n");
employees2[0].Location = "Botswana"; // this will work! because both arrays contain references to object (shallow copy)
// To iterate we need to type cast (Employee[])
Employee[] spainEmployees2 = (Employee[])spainEmployees.Clone(); // creates an array 6 copys from source to that new array
foreach(Employee item in spainEmployees2) //employees3 will get you an error because clone method return type is an object
{
if (!(item is null))
{
Console.WriteLine(item.Location);
}
else
{
Console.WriteLine("null object");
}
}
Foreach source array we will copy accross to destination array
if source has 4 destination will have 4
If you want (duplicate) independant objects in your source use deep copy
if you want to pass reference and change use shallow copy
There is no predefined method for deep copy we need to use Icloneable
The CreateNewEmployee
method should be called Clone
which means creating new object with the same data
To take this one step further it is recommended for clarity to implement the interface
Class Employee:System.ICloneable
This means we should change our type topublic object Clone()
this is because we are following the microsoft object type. In addition you will need to type cast the object were creating as class type(Employee)employees[i].Clone()
First this will return the object which we then need to cast properly as an instance of Employee
using System;
namespace CopyAndClone
{
//model class
class Employee : ICloneable
{
public string EmployeeName { get; set; }
public string Role { get; set; }
public object Clone()
{
//this will refer to first object if we employees[0].CreateNewEmployee and copy all the data
Employee new_one = new Employee() { EmployeeName = this.EmployeeName, Role = this.Role };
return new_one;
}
}
class Program
{
static void Main()
{
Employee[] employees = new Employee[]
{
new Employee() { EmployeeName = "Joseph", Role = "Developer" },
new Employee() { EmployeeName = "Jack", Role = "Designer" },
new Employee() { EmployeeName = "Alexa", Role = "Analyst" }
};
Employee[] employees_deep_copy = new Employee[employees.Length];
for(int i = 0; i<employees.Length;i++)
{
//Console.WriteLine(employees[i].EmployeeName);
var result = (Employee)employees[i].Clone(); //creating new object with the same data as we are using icloneable interface and only specified as object we need to further typecast as Employee
employees_deep_copy[i] = result; // manual implementation of deepcopy
}
//print deep copy
Console.WriteLine("Deep Copy: \n");
// if we were now to make changes to employees array it would not be affected in employees_deep_copy
employees[1].EmployeeName = "Bill";
foreach (Employee emp in employees_deep_copy)
{
if (!(emp is null))
{
Console.WriteLine(emp.EmployeeName + ", " + emp.Role);
}
else
{
Console.WriteLine("null object");
}
}
//new array
Employee[] highlyPaidEmployees = new Employee[5];
//CopyTo
employees.CopyTo(highlyPaidEmployees, 2);
employees[0].Role = "Changed";
//print destination array
Console.WriteLine("\nCopyTo:");
foreach (Employee emp in highlyPaidEmployees)
{
if (!(emp is null))
{
Console.WriteLine(emp.EmployeeName + ", " + emp.Role);
}
else
{
Console.WriteLine("null object");
}
}
//Clone
Employee[] highlyPaidEmployees2 = (Employee[])employees.Clone(); //createss a new array & copies from source array to that new array
Console.WriteLine("\nClone:");
foreach (Employee emp in highlyPaidEmployees2)
{
if (!(emp is null))
{
Console.WriteLine(emp.EmployeeName + ", " + emp.Role);
}
else
{
Console.WriteLine("null object");
}
}
Console.ReadKey();
}
}
}
class Program
{
public static int[] ArrayOfMultiples(int num, int length)
{
length = length + 1;
int[] newArray = new int[length];
for (int i = 0; i < length; i++)
{
newArray[i] = num * i;
}
return newArray[1..];
}
static void Main()
{
foreach(int item in ArrayOfMultiples(7, 5))
{
Console.WriteLine(item);
}
Console.WriteLine("\n");
foreach (int item in ArrayOfMultiples(12, 10))
{
Console.WriteLine(item);
}
}
}
Collections are the standard-way to store and manipulate group of elements (primitive values or objects).
- Collections are internally objects of specific* collection classes* such as List, Dictionary, SortedList etc.
Syntax: List<type> referenceVariable = new List<type>( )
(see pdf)[_resources\Collections+Cheatsheet.pdf]
- Collections can store unlimited elements.
- You can add, remove elements at any time.
- You need not specify the size (no. of elements) while creating collection.
- You can search, sort, copy collections using various built-in methods.
List collection contains a group of elements of same type. βΊ Full Path: System.Collections.Generic.List βΊ The 'List' class is a generic class; so you need to specify data type of value while creating object. (see pdf)[_resources_resources/List+Cheatsheet.pdf']
- required to import System.Collections.Generic
using System;
using System.Collections.Generic;
namespace ListExample
{
class Program
{
static void Main()
{
// Create a reference valirable for list class
List<int> myList = new List<int>() { 10, 20, 30 }; //stored in stack of main method //Specify data type as its generic class, is created in heap
foreach (int item in myList)
{
Console.WriteLine(item);
}
}
}
}
- used to add single elements,
- add range is used for multiple
Syntax:
public void AddRange (System.Collections.Generic.IEnumerable<T> collection);
Add( T ) This method is used to add an object at the end of the list. ... Clear() This method is used to remove all the elements from the list. ... Insert( Int32, T ) This method is used to insert an element at the specified position in the list. ... RemoveAt( Int32 ) ... Sort()
using System;
using System.Collections.Generic;
namespace ListExample
{
class Program
{
static void Main()
{
// Create a reference valirable for list class
List<int> myList = new List<int>() { 10, 20, 30 }; //stored in stack of main method //Specify data type as its generic class, is created in heap
myList.Add(40); // add single
myList.AddRange(myList);//Adding range to collections
foreach (int item in myList)
{
Console.WriteLine(item);
}
}
}
}
Syntax: public void Insert (int index, T item);
Syntax: public virtual void InsertRange (int index, ICollection element);
using System;
using System.Collections.Generic;
namespace ListExample
{
class Program
{
static void Main()
{
// Create a reference valirable for list class
List<int> myList = new List<int>() { 10, 20, 30 };
myList.Insert(8,100);//Inserting
myList.InsertRange(9, myList);//Inserting
foreach (int item in myList)
{
Console.WriteLine(item);
}
}
}
}
Cleaning up arrays
Syntax: public bool Remove (T item);
Syntax: public void RemoveAt (int index);
Syntax: public void RemoveRange (int index, int count);
Syntax: public int RemoveAll (Predicate<T> match);
Syntax: public void Clear ();
using System;
using System.Collections.Generic;
namespace ListExample
{
class Program
{
private static bool EndsWithFive(int i)
{
return i % 10 == 5;
}
static void Main()
{
// Create a reference valirable for list class
List<int> myList = new List<int>() { 10, 20, 30, 40, 50, 65, 75, 85, 100 }; //stored in stack of main method //Specify data type as its generic class, is created in heap
//myList.Remove(40); //Remove by value
//myList.RemoveAt(0); //Remove by index
//myList.RemoveRange(1,7); //Remove by index, count
// Search predicate returns true if a string ends in "5".
myList.RemoveAll(EndsWithFive); // uses method to check using an iteration
myList.Clear(); //clears list
foreach (int item in myList)
{
Console.WriteLine(item);
}
}
}
}
using System;
using System.Collections.Generic;
namespace ListExample
{
class Program
{
static void Main()
{
// Create a reference valirable for list class
List<int> myList = new List<int>() { 10, 20, 30, 40, 50, 65, 75, 85, 100 }; //stored in stack of main method //Specify data type as its generic class, is created in heap
Console.WriteLine(myList.IndexOf(30)); // Search for value return index - 1 if not found
Console.WriteLine(myList.BinarySearch(40)); // It works by repeatedly dividing in half the portion of the list that could contain the item
Console.WriteLine(myList.Contains(65)); // returns bool
foreach (int item in myList)
{
Console.WriteLine(item);
}
}
}
}
using System;
using System.Collections.Generic;
namespace ListExample
{
class Program
{
static void Main()
{
// Create a reference valirable for list class
List<int> myList = new List<int>() { 50, 20, 10, 30, 40, 65, 75, 85, 100 }; //stored in stack of main method //Specify data type as its generic class, is created in heap
myList.Sort(); // Sorts numerically or alphabetically
myList.Reverse(); // Reverses array
foreach (int item in myList)
{
Console.WriteLine(item);
}
}
}
}
using System;
using System.Collections.Generic;
namespace ListExample
{
class Program
{
static void Main()
{
// Create a reference valirable for list class
List<int> myList = new List<int>() { 50, 20, 10, 30, 40, 65, 75, 85, 100 }; //stored in stack of main method //Specify data type as its generic class, is created in heap
myList.ToArray(); //convert to array
myList.ForEach(item =>{ Console.WriteLine(item);}); //Foreach Lambda
}
}
Exists executes labda expression once per each element
Syntax: public bool Exists (Predicate<T> match);
Syntax: public T? Find (Predicate<T> match);
Syntax: public T? FindLast (Predicate<T> match);
The Predicate delegate that defines the conditions of the element to search for.
using System;
using System.Collections.Generic;
namespace ListExample
{
class Player
{
public string ?Name{get;set;}
public int Rank{get; set;}
}
class Program
{
static void Main()
{
// Create a reference valirable for list class
List<Player> players = new List<Player>();
// Add parts to the list.
players.Add(new Player() { Name = "friki", Rank = 116 });
players.Add(new Player() { Name = "vmax", Rank = 402 });
players.Add(new Player() { Name = "killmr", Rank = 103 });
players.Add(new Player() { Name = "Noe Grimes", Rank = 116 });
players.Add(new Player() { Name = "edcat08", Rank = 500 });
players.Add(new Player() { Name = "DeathCoreta", Rank = 116 });
Console.WriteLine(players.Exists(x => x.Rank == 500)); // predicate function Exists returns true or false
Console.WriteLine(players.Find(player => player.Name == "friki")?.Rank); // predicate function will Find item in list and return the object
Console.WriteLine(players.FindIndex(player => player.Name == "edcat08")); //simply return the index
Console.WriteLine(players.FindLast(player => player.Rank == 116)?.Name); //based on predicate cond get last item return object
Console.WriteLine(players.FindLastIndex(player => player.Rank == 116)); //based on predicate cond get last item return index
foreach (Player items in players.FindAll(player => player.Rank == 116)) //based on predicate cond return all
{
Console.WriteLine(items.Name);
}
}
}
}
Exectures the lambda expression once per each element adds returned element into a new collection and reutns the same at last. thus it converts all elements from the input collection as output collection
Syntax: public System.Collections.Generic.List<TOutput> ConvertAll<TOutput> (Converter<T,TOutput> converter);
using System;
using System.Collections.Generic;
namespace ListExample
{
class Player
{
public string ?Name{get;set;}
public int Rank{get; set;}
}
class Program
{
static void Main()
{
// Create a reference valirable for list class
List<Player> players = new List<Player>();
// Add parts to the list.
players.Add(new Player() { Name = "friki", Rank = 116 });
players.Add(new Player() { Name = "vmax", Rank = 402 });
players.Add(new Player() { Name = "killmr", Rank = 103 });
players.Add(new Player() { Name = "Noe Grimes", Rank = 116 });
players.Add(new Player() { Name = "edcat08", Rank = 500 });
players.Add(new Player() { Name = "DeathCoreta", Rank = 116 });
List<int> lengths = players.ConvertAll(s => s.Name.Length); //convert all from one type to another
foreach (int item in lengths ) //based on predicate cond return all
{
Console.WriteLine(item);
}
}
}
}
Dictionary collection contains a group of elements of key/value pairs List is index based when dictionary is key based
- System.Collections.Generic
Syntax:
Dictionary<TKey, TValue>
using System;
using System.Collections.Generic;
namespace ListExample
{
class Program
{
static void Main()
{
//creating a dictionary using collection-initializer syntax
var deviceTypes = new Dictionary<string, string>(){
{"Mac", "Ipad, Iphone, Ipad"},
{"Windows", "Del, Acer, Gigabyte"},
{"Android", "Galaxy , Note"}
};
Console.WriteLine(deviceTypes["Mac"]); //prints value of UK key
}
}
Sorted list is better when you want to work with large objects/list -requires using System.Collections.Generic;
Syntax: public class SortedList : ICloneable, System.Collections.IDictionary
To roughly summarize, you want a SortedList<K, V>
Good if:
- you require indexed look-up.
- it's desirable to have lesser memory overhead.
- your input data is already sorted (say you get it already ordered from db).
using System;
using System.Collections;
namespace ListExample
{
class Program
{
public static void PrintKeysAndValues(SortedList myList)
{
Console.WriteLine("\t-KEY-\t-VALUE-");
for (int i = 0; i < myList.Count; i++)
{
Console.WriteLine("\t{0}:\t{1}", myList.GetKey(i), myList.GetByIndex(i));
}
Console.WriteLine();
}
static void Main()
{
//creating a dictionary using collection-initializer syntax
SortedList mySortedList = new SortedList();
mySortedList.Add("One", "friki");
mySortedList.Add("Two", "Noe Grimes");
Console.WriteLine(mySortedList.GetKey(1));//goes to key directly
PrintKeysAndValues(mySortedList);
}
}
}
Dictionary / Sorted list are both generic class Good if: you need to access elements by using key, and you can identify a useful key value. Very quick particularly efficient when the maximum number of entries can be predicted in advance. Calculated based on the hashcode of the key
using System;
using System.Collections;
namespace ListExample
{
class Program
{
static void Main()
{
//creating a Hashtable using collection-initializer syntax
var deviceTypes = new Hashtable(){
{"Mac", "Ipad, Iphone, Ipad"},
{"Windows", "Del, Acer, Gigabyte"},
{"Android", "Galaxy , Note"}
};
Console.WriteLine(deviceTypes["Mac"]);
foreach (DictionaryEntry deviceType in deviceTypes)
{
Console.WriteLine(deviceType.Value);
}
}
}
}
Hashset collection contains a group of elements of unique values stored at respective indexes. Hashset will be calculated based on the hascode of the value (see pdf)[_resources/Hashset+Cheatsheet.pdf]
The "HashSet" class is a generic class.
- You can't set / get the element based on the key / index.
- It searches elements based on the index generated based on the search value.
- HashSet allows only one null value; Hashtable allows only one null key; but allows multiple null values.
- You can't access elements based on key / index. You can use Contains method to search for an element.
- You can't sort elements in HashSet.
- Elements must be unique; duplicate elements are not allowed
using System;
using System.Collections;
namespace ListExample
{
class Program
{
static void Main()
{
//creating an array of strings
string[] deviceTypes = new string[] {
"Ipad, Iphone, Ipad",
"Del, Acer, Gigabyte",
"Galaxy , Note",
"Del, Acer, Gigabyte",
"Del, Acer, Intel"
};
HashSet<string> hashSet = new HashSet<string>(deviceTypes); //unique set of values based on hashcode
foreach (string uniqueDevices in hashSet)
{
Console.WriteLine(uniqueDevices);
}
}
}
}
List: Good if: You want to store a plain list of elements of a particular type (when all values are same type) the use list ArrayList: Good if: You dont know what types of values to store use *array list (elements can be of anytype)
- array is not generic class so we do not need to specify data type
using System;
using System.Collections;
namespace ListExample
{
class Program
{
static void Main()
{
var arrayList = new ArrayList(); // no need to specify type ArrayList is good for mixed values
arrayList.Add(1);
arrayList.Add("two");
arrayList.Add('a');
arrayList.Add(2.5);
foreach (object item in arrayList)
{
Console.WriteLine(item);
}
}
}
}
stack colleciton contains a group of elements based on LIFO Last in first out
using System;
using System.Collections;
namespace ListExample
{
class Program
{
static void Main()
{
Stack<int> myStack = new Stack<int>(); //must specify type
myStack.Push(1);
myStack.Push(2);
myStack.Push(3);
myStack.Push(4);
int r = 0;
foreach (int item in myStack)
{
r++;
Console.WriteLine(item + " ."+r); // prints last first lifo
}
}
}
Queue works the same as stack but in reverse FIFO Syntax:
public class Queue : ICloneable, System.Collections.ICollection
Queue<T> ref = new Queue<T>()
using System;
using System.Collections;
namespace ListExample
{
class Program
{
static void Main()
{
Queue<string> myQueue = new Queue<string>(); //must specify type
myQueue.Enqueue("Hello"); //note use of enqueue method
myQueue.Enqueue("World"); //note use of enqueue method
myQueue.Enqueue("!"); //note use of enqueue method
foreach (string item in myQueue)
{
Console.Write(item + " "); // prints last first fifo
}
}
}
}
A collection object where each element stores a reference to some other object
Syntax: List<ClassName> referenceVar = new List <ClassName>()
using System;
using System.Collections;
namespace ListExample
{
class Student
{
public int StuId { get; set; }
public string ?Name { get; set; }
public int? FacId { get; set; }
}
class Faculty
{
public int FacId { get; set; }
public string Location { get; set; }
public string? Name { get; set; }
}
class Program
{
static void Main()
{
Student s1 = new Student();
s1.StuId = 1;
s1.Name = "Joey";
s1.FacId = 3;
Student s2 = new Student();
s2.StuId = 2;
s2.Name = "Jacobo";
s2.FacId = 3;
Student s3 = new Student() { StuId = 3, Name = "Luis", FacId = 1};
Faculty f1 = new Faculty() { FacId = 3, Name = "MAU", Location = "Malaga" };
Faculty f2 = new Faculty() { FacId = 1, Name = "MDU", Location = "Madrid" };
List<Student> schoolInfo = new List<Student>(); // a collection of references
List<Faculty> facInfo = new List<Faculty>(); // a collection of references
schoolInfo.Add(s1); //note use of add
schoolInfo.Add(s2);
schoolInfo.Add(s3);
foreach(Student item in schoolInfo)
{
if (item.FacId == 3)
{
Console.WriteLine(item.Name);
}
}
facInfo.Add(f1); //note use of add
facInfo.Add(f2); //note use of add
foreach (Faculty item in facInfo)
{
Console.WriteLine(item.Name);
}
}
}
}
//Harsha's example creating/adding to a collection using do and readline
using System;
using ECommerce;
using System.Collections.Generic;
namespace CollectionOfObjectsExample
{
class Program
{
static void Main()
{
//create an empty collection
List<Product> products = new List<Product>();
//loop to read data from user
string choice;
do
{
Console.Write("Enter Product ID: ");
int pid = int.Parse(Console.ReadLine());
Console.Write("Enter Product Name: ");
string pname = Console.ReadLine();
Console.Write("Enter Price: ");
double unitPrice = double.Parse(Console.ReadLine());
Console.Write("Enter Date of Manufacture (yyyy-MM-dd): ");
DateTime dom = DateTime.Parse(Console.ReadLine());
//Create a new object of Product class
Product product = new Product() { ProductID = pid, ProductName = pname, Price = unitPrice, DateOfManufacture = dom };
//Add Product object to the collection
products.Add(product);
//Ask the user to continue
Console.WriteLine("Product Added.\n");
Console.WriteLine("Do you want to continue to next product? (Yes/No)");
choice = Console.ReadLine();
} while (choice != "No" && choice != "no" && choice != "n" && choice != "N");
//foreach
Console.WriteLine("\nProducts:");
foreach (Product item in products)
{
Console.WriteLine(item.ProductID + ", " + item.ProductName + ", " + item.Price + ", " + item.DateOfManufacture.ToShortDateString());
}
Console.ReadKey();
}
}
}
An object can contain a field that stores references to one or more objects
(see pdf)[_resources/Object+Relations+Cheatsheet.pdf]
One-to-one, One-to-many, many-to-many;
using System;
using System.Collections;
namespace OneToOneRelationExample
{
class Player
{
public int Id { get; set; }
public string ?Name { get; set; }
public Stats ?GetStats { get; set; } //One single object stats relates to player
public void Print(){
Console.WriteLine("Name: " + this.Name);
Console.WriteLine("Total Kills: " + this.GetStats.Kills);
Console.WriteLine("Total Assists: " + this.GetStats.Assists);
Console.WriteLine("Total Deaths: " + this.GetStats.Deaths);
Console.WriteLine("Kill Death Ratio : " + this.GetStats.KillRatio());
}
}
class Stats
{
public int Kills { get; set; }
public int Assists { get; set; }
public int Deaths { get; set; }
public double KillRatio()
{
return (Kills + Assists) / Deaths;
}
}
class Program
{
static void Main()
{
Player p = new Player();
p.Name = "edcat08";
Stats st = new Stats();
st.Kills = 32;
st.Assists = 3;
st.Deaths = 7;
p.GetStats =st; //Initialize the Stats through Player and pass the object instance
p.Print();
}
}
}
using System;
using System.Collections;
namespace OneToManyRelationExample
{
class Player
{
List<GameStats> gList = new List<GameStats>();
public int Id { get; set; }
public string ?Name { get; set; }
public List<GameStats> GetGameStats //Multiple Game objects relates to one person
{
get{ return gList;}
set{gList = value;}
}
public void Print(){
Console.WriteLine("Name: " + this.Name);
foreach(GameStats game in this.GetGameStats)
{
Console.WriteLine("Postion: " + game.WininngGame());
}
}
}
class GameStats
{
public int Id { get; set; }
public int Position { get; set; }
public int TotalPlayers { get; set; }
public bool WininngGame()
{
if(Position == 1) { return true; } else { return false; };
}
}
class Program
{
static void Main()
{
//Player p = new Player();
List<GameStats> GameList = new List<GameStats>();
GameStats g1 = new GameStats() { Id = 100000, Position = 1, TotalPlayers = 260};
GameStats g2 = new GameStats() { Id = 200000, Position = 12, TotalPlayers = 160 };
GameStats g3 = new GameStats() { Id = 300000, Position = 3, TotalPlayers = 160 };
GameList.Add(g1);
GameList.Add(g2);
GameList.Add(g3);
Player p = new Player() { Id = 30, Name = "edcat08" };
p.GetGameStats = GameList;
p.Print();
}
}
}
using System;
using System.Collections.Generic;
namespace ManyToOneExample
{
class Operator
{
public string Gender { get; set; }
public string Name { get; set; }
public Faction Faction { get; set; }
public void Print()
{
Console.Write("Operator belongs to Faction: " + this.Faction.FactionName + " ");
}
}
class Faction
{
public int? FactionId { get; set; }
public string? FactionName { get; set; }
}
class Program
{
static void Main()
{
//Three operators belonging to one player
Operator o1 = new Operator() { Gender = "m", Name = "Golem", };
Operator o2 = new Operator() { Gender = "m", Name = "Ronin", };
Operator o3 = new Operator() { Gender = "f", Name = "Mara", };
//create object of Department class
Faction faction = new Faction() { FactionId = 10, FactionName = "Warcom" };
o1.Faction = faction;
o2.Faction = faction;
o3.Faction = faction;
Console.WriteLine("FIRST Operator:");
Console.WriteLine("Operator Gender: " + o1.Gender);
Console.WriteLine("Operator Name: " + o1.Name);
Console.WriteLine("Faction ID: " + o1.Faction.FactionId);
Console.WriteLine("Faction Name: " + o1.Faction.FactionName);
Console.WriteLine("Second Operator:");
Console.WriteLine("Operator Gender: " + o2.Gender);
Console.WriteLine("Operator Name: " + o2.Name);
Console.WriteLine("Faction ID: " + o2.Faction.FactionId);
Console.WriteLine("Faction Name: " + o2.Faction.FactionName);
Console.WriteLine("Third Operator:");
Console.WriteLine("Operator Gender: " + o3.Gender);
Console.WriteLine("Operator Name: " + o3.Name);
Console.WriteLine("Faction ID: " + o3.Faction.FactionId);
Console.WriteLine("Faction Name: " + o3.Faction.FactionName);
}
}
}
Array list, stack, queue, list etc are all inherited from parent classes (interfaces) thats why as devs its good to know these. Its good to know the predefined interfaces. IEnumerator has two IEnumerator and the interface IEnumerator (you do not need to specify datatype)
class List<t>
can for example be stored in interface IList or interface Icollection as these are all the parents. You could also implement class Dictionary
of interface Icollection
You cant store dictionary reference in Ilist for example as they dont have same parent
In short a child object is compatible with all its corresponding parent interfaces present in the hierachy
Good for storing the reference of a specific type. If you store as (generic) this will give you complete flexibility
Here you can the hierarchy: public interface IList : System.Collections.ICollection
a simple search will reveal all the information you need (ilist)[https://docs.microsoft.com/en-us/dotnet/api/system.collections.ilist?view=net-6.0]
IEnumerable is an interface that defines one method GetEnumerator which returns an IEnumerator interface, this in turn allows readonly access to a collection. A collection that implements IEnumerable can be used with a foreach statement.
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
public interface IEnumerator
{
object Current { get; }
bool MoveNext();
void Reset();
}
These two are not compatibile. Each has its own purpose and uses.
(see pdf)[_resources/IEnumerable+vs+IEnumerator+Cheatsheet.pdf]
List for example has the same type as List<T> : ICollection<T>, IEnumerable<T>, IEnumerable, IList<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection, IList
by right clicking and inspecting List you can see the interfaces you can implement wit this refernce type.
IEnumerable
using System;
using System.Collections.Generic;
using System.Linq;
namespace IEnumerableExample
{
class Program
{
static void Main()
{
//create a collection
IEnumerable<string> messages; // Storing ref from parents will give you flexibility βΊ It is the parent interface of all types of collections.
messages = new List<string>() { "How are you", "Have a great day", "Thanks for meeting" }; // benefit you can store not just the ref of list but also stack etc!
//foreach
Console.WriteLine("IEnumerable:"); // something multiple somthing plural - this process is same as messages.GetEnumerator below. but implemented automatically (its good to know whats going on behind scenes)
foreach (string item in messages)
{
Console.WriteLine(item);
}
//IEnumerator this is made for reading through multiple (makes the foreach loop work)
Console.WriteLine("\nIEnumerator:");
IEnumerator<string> enumerator = messages.GetEnumerator(); //this assigns into the IEnumerator we dont need to implement this(it happens automatically)
enumerator.Reset();
while (enumerator.MoveNext()) // bool val
{
Console.WriteLine(enumerator.Current); //How are you
}
Console.ReadKey();
}
}
}
Iterator is a method which yield returns elements one-by-one.
- Iterator is used to iterate over a set of elements.
- The yield return statement returns an element; but pauses execution of the method _resources/Iterator+Cheatsheet.pdf
Using yield to define an iterator removes the need for an explicit extra class (the class that holds the state for an enumeration, see IEnumerator for an example) when you implement the IEnumerable and IEnumerator pattern for a custom collection type. The following example shows the two forms of the yield statement.
Iterators can be used for:
- Performing an action on each item in a collection.
- Enumerating a custom collection.
- Extending LINQ or other libraries.
- Creating a data pipeline where data flows efficiently through iterator methods.
// C# program to illustrate the concept
// of iterator using list collection
using System;
using System.Collections.Generic;
class IteratorExample
{
static int counter = 1;
public static IEnumerable<string> GetMyList() //Iterator method
{
// Creating and adding elements in list
List<string> playerList = new List<string>() {"edcat08", "noegrimes", "friki", "vmax" };
counter = 0;
// Iterating the elements of my_list
foreach (var items in playerList)
{
// Console.WriteLine("Iterator method executes");
// Returning the element after every iteration
yield return items.ToUpper(); //remains of list type
// Console.WriteLine("Iterator method continues");
counter++;
}
}
// Main Method
static public void Main()
{
// Storing the elements of GetMyList
IEnumerable<string> my_slist = GetMyList();
var enumerator1 = my_slist.GetEnumerator();
enumerator1.MoveNext();
Console.WriteLine("Current " + enumerator1.Current);
enumerator1.MoveNext(); // Here you can use this to break up your operations if you have more things to do with your code
Console.WriteLine("Lobby Count " + IteratorExample.counter);
enumerator1.MoveNext();
Console.WriteLine("Current "+ enumerator1.Current);
enumerator1.MoveNext();
Console.WriteLine("Lobby Count " + IteratorExample.counter);
Console.WriteLine("Current " + enumerator1.Current);
//Console.WriteLine("In a loop \n");
// Display the elements return from iteration
//foreach (var i in my_slist)
//{
// Console.WriteLine(i);
//}
}
}
You can highlight code types and right click rename to rename all of your variables say for example.
Say you have a bunch of objects in your collection and you want to apply custom methods to each of these objects. Use custom collections. This generally should inherit from IEnumerable (see pdf)[_resources/Custom+Collections+Cheatsheet.pdf]
//example note we have to follow IEnumerable interface
public class ClassName : IEnumerable
{
private List<yourClassName> list = new List<yourClassName>();
public IEnumerator GetEnumerator()
{
for (int index = 0; index < list.Count; index++)
{
yield return list[index];
}
}
}
//full implementation
using System;
using System.Collections.Generic;
using System.Collections;
namespace CustomCollections
{
public enum TypeOfPlayer
{
RegularPlayer,PayingPlayer
}
public class Player
{
public string Id { get; set; }
public string Name { get; set; }
public TypeOfPlayer TypePlayer { get; set; }
}
//custom collection class will give you more grip on the methods to extend the custom ones.. (see Add method below)
class PlayerList
{
private List<Player> players = new List<Player>();
//public List<Player> GetPlayers() // not recommended we need method to access this private field
//{
// return players;
//}
// as a custom collection
//better to use IEnumerable
public IEnumerator GetEnumerator() //required using System.Collections; as we are NOT using the generic class
{
for(int i = 0; i < players.Count; i++)
{
yield return players[i];
}
}
//could manipultate more with Add(), Find(), Remove()
//lets add a user defined method
public void Add(Player player)
{
//custom Add logic
if (player.TypePlayer == TypeOfPlayer.PayingPlayer)
{
players.Add(player); // simply add to the list
} else
{
Console.WriteLine("Sorry only regular players allowed");
}
}
}
class Program
{
static void Main()
{
PlayerList playerList = new PlayerList();
playerList.Add(new Player(){Id = "7",Name="killmr", TypePlayer=TypeOfPlayer.RegularPlayer }); // note the use of our custom method inside custom collection class
playerList.Add(new Player() { Id = "9", Name = "NOE Grimes", TypePlayer = TypeOfPlayer.RegularPlayer });
//we now can add lots more dynamically aka object intitializer
playerList.Add(new Player() { Id = "1", Name = "edcat08", TypePlayer = TypeOfPlayer.PayingPlayer });
playerList.Add(new Player() { Id = "2", Name = "vmax", TypePlayer = TypeOfPlayer.PayingPlayer });
playerList.Add(new Player() { Id = "3", Name = "friki", TypePlayer = TypeOfPlayer.RegularPlayer });
//IEnumerator enumerator = playerList.GetEnumerator(); // note declare of IEnumerator Type
//MoveNext() implementation is not particularly real world
//better to use foreach (IEnumerator IEnumerable & Enumerator here are working together)
foreach (Player item in playerList) // no need to call enumerator as this method is built into
{
Console.WriteLine(item.Name);
}
}
}
}
It is better to use IEnumerable generic class so we can convey that we want to store only a list of a particular type of object.
using System;
using System.Collections.Generic;
using System.Collections;
namespace CustomIEnumerable
{
public enum TypeOfPlayer
{
RegularPlayer, PayingPlayer
}
public class Player
{
public string Id { get; set; }
public string Name { get; set; }
public TypeOfPlayer TypePlayer { get; set; }
}
//custom collection generic class IEnumerable
class PlayerList:IEnumerable<Player>
{
private List<Player> players = new List<Player>();
//implementing IEnumerable.GetEnumerator()
//this method will get called first as its the direct implementaion of IEnumerable interface
IEnumerator IEnumerable.GetEnumerator() // explicit interface implementation by default this has to be private
{
return this.GetEnumerator();//from here we call the below method return type is IEnumerator
}
//implementing IEnumerable<T> get enumerator
public IEnumerator<Player>GetEnumerator() // The problem with this is it doesnt implement defined methods like Add(),Find()
{
for (int i = 0; i < players.Count; i++)
{
yield return players[i];
}
}
//could manipultate more with Add(), Find(), Remove()
//lets add a user defined method
public void Add(Player player)
{
//custom Add logic
if (player.TypePlayer == TypeOfPlayer.PayingPlayer)
{
players.Add(player); // simply add to the list
}
else
{
Console.WriteLine("Sorry only regular players allowed");
}
}
}
class Program
{
static void Main()
{
PlayerList playerList = new PlayerList();
playerList.Add(new Player() { Id = "7", Name = "killmr", TypePlayer = TypeOfPlayer.RegularPlayer }); // note the use of our custom method inside custom collection class
playerList.Add(new Player() { Id = "9", Name = "NOE Grimes", TypePlayer = TypeOfPlayer.RegularPlayer });
//we now can add lots more dynamically aka object intitializer
playerList.Add(new Player() { Id = "1", Name = "edcat08", TypePlayer = TypeOfPlayer.PayingPlayer });
playerList.Add(new Player() { Id = "2", Name = "vmax", TypePlayer = TypeOfPlayer.PayingPlayer });
playerList.Add(new Player() { Id = "3", Name = "friki", TypePlayer = TypeOfPlayer.RegularPlayer });
//IEnumerator enumerator = playerList.GetEnumerator(); // note declare of IEnumerator Type
//MoveNext() implementation is not particularly real world
//better to use foreach (IEnumerator IEnumerable & Enumerator here are working together)
foreach (Player item in playerList) // no need to call enumerator as this method is built into
{
Console.WriteLine(item.Name);
}
}
}
}
You can also implement your custom class with a type called ICollection.
You can right click the Interface name and select implement interface this will add all the required methods for you
Delegates: Holds the reference to a method. The reference can be changed at runtime. Good for events and the call-back methods.
using System;
using System.Collections.Generic;
using System.Collections;
namespace CustomICollection
{
public enum TypeOfPlayer
{
RegularPlayer, PayingPlayer
}
public class Player
{
public string ? Id { get; set; }
public string ? Name { get; set; }
public TypeOfPlayer TypePlayer { get; set; }
}
//custom collection generic class ICollection
class PlayerList : ICollection<Player> //Needs to implement all methods in ICollection
{
private List<Player> players = new List<Player>();
public int Count => players.Count; // count of items present in collection
public bool IsReadOnly => false; // we want users to be able to modify this collection returns either true or false if found
//implementing IEnumerable.GetEnumerator()
//this method will get called first as its the direct implementaion of IEnumerable interface
IEnumerator IEnumerable.GetEnumerator() // explicit interface implementation by default this has to be private
{
return this.GetEnumerator();//from here we call the below method return type is IEnumerator
}
//implementing IEnumerable<T> get enumerator
public IEnumerator<Player> GetEnumerator() // The problem with this is it doesnt implement defined methods like Add(),Find()
{
for (int i = 0; i < players.Count; i++)
{
yield return players[i];
}
}
//could manipultate more with Add(), Find(), Remove()
//lets add a user defined method
public void Add(Player player)
{
//custom Add logic
if (player.TypePlayer == TypeOfPlayer.PayingPlayer)
{
players.Add(player); // simply add to the list
}
else
{
Console.WriteLine("Sorry only paying players allowed");
}
}
public void Clear()
{
// throw new NotImplementedException();
}
public bool Contains(Player item)
{
return players.Contains(item);
}
public void CopyTo(Player[] array, int arrayIndex)
{
// throw new NotImplementedException();
players.CopyTo(array, arrayIndex);
}
public bool Remove(Player item)
{
return players.Remove(item);
}
// can go on to add additional methods
public Player Find(Predicate<Player>match) // this predicate returns bool value
{
return players.Find(match); //invokes the delegate object for each element in the list and returns first matching
}
public List<Player> FindAll(Predicate<Player>match) //return type is list of customer
{
return players.FindAll(match);
}
}
class Program
{
static void Main()
{
PlayerList playerList = new PlayerList();
playerList.Add(new Player() { Id = "7", Name = "killmr", TypePlayer = TypeOfPlayer.RegularPlayer }); // note the use of our custom method inside custom collection class
playerList.Add(new Player() { Id = "9", Name = "NOE Grimes", TypePlayer = TypeOfPlayer.PayingPlayer });
//we now can add lots more dynamically aka object intitializer
playerList.Add(new Player() { Id = "1", Name = "edcat08", TypePlayer = TypeOfPlayer.PayingPlayer });
playerList.Add(new Player() { Id = "2", Name = "vmax", TypePlayer = TypeOfPlayer.PayingPlayer });
playerList.Add(new Player() { Id = "3", Name = "friki", TypePlayer = TypeOfPlayer.PayingPlayer });
Player new_player = new Player() { Id = "10", Name = "Jacob", TypePlayer = TypeOfPlayer.PayingPlayer };
playerList.Add(new_player);
//Contains
Console.WriteLine("Contains: " + playerList.Contains(new_player));
Console.WriteLine(playerList.Count + " customers found.");
//Remove
playerList.Remove(new_player);
foreach (Player player in playerList)
{
Console.WriteLine(player.Id + ", " + player.Name + ", " + player.TypePlayer);
}
////Find
Player matchingPlayer = playerList.Find(x => x.Id == "1");
if (matchingPlayer != null)
{
Console.WriteLine("Matching Player Found: " + matchingPlayer.Name + ", " + matchingPlayer.Id);
}
////FindAll
List<Player> paying_players = playerList.FindAll(player => player.TypePlayer == TypeOfPlayer.PayingPlayer);
Console.WriteLine("Paying players:");
foreach (Player player in paying_players)
{
Console.WriteLine(player.Id + ", " + player.Name + ", " + player.TypePlayer);
}
playerList.Clear();
Console.ReadKey();
foreach (Player item in playerList) // no need to call enumerator as this method is built into
{
Console.WriteLine(item.Name);
}
}
}
}
Note: if you target framework does not match you application at runtime will not update. See this (guide to change)[https://docs.microsoft.com/en-us/dotnet/core/tools/sdk-errors/netsdk1045?f1url=%3FappId%3DDev16IDEF1%26l%3DEN-US%26k%3Dk(NETSDK1045)%26rd%3Dtrue]
On top right col you can see all the methods that pertain to your assembly
Try creating a custom collection based on dictionary
using System;
using System.Collections.Generic;
using System.Collections;
namespace CustomCollectionIList
{
public enum TypeOfPlayer
{
RegularPlayer, PayingPlayer
}
public class Player
{
public string? Id { get; set; }
public string? Name { get; set; }
public TypeOfPlayer TypePlayer { get; set; }
}
//custom collection generic class ICollection
class PlayerList : IList<Player> //Needs to implement all methods in ICollection
{
private List<Player> players = new List<Player>();
public int Count => players.Count; // count of items present in collection
public bool IsReadOnly => false; // we want users to be able to modify this collection returns either true or false if found
public Player this[int index] { get => players[index]; set => players[index] = value; } //indexer with get /set accesor
//implementing IEnumerable.GetEnumerator()
//this method will get called first as its the direct implementaion of IEnumerable interface
IEnumerator IEnumerable.GetEnumerator() // explicit interface implementation by default this has to be private
{
return this.GetEnumerator();//from here we call the below method return type is IEnumerator
}
//implementing IEnumerable<T> get enumerator
public IEnumerator<Player> GetEnumerator() // The problem with this is it doesnt implement defined methods like Add(),Find()
{
for (int i = 0; i < players.Count; i++)
{
yield return players[i];
}
}
//could manipultate more with Add(), Find(), Remove()
//lets add a user defined method
public void Add(Player player)
{
//custom Add logic
if (player.TypePlayer == TypeOfPlayer.PayingPlayer)
{
players.Add(player); // simply add to the list
}
else
{
Console.WriteLine("Sorry only paying players allowed");
}
}
public void Clear()
{
// throw new NotImplementedException();
}
public bool Contains(Player item)
{
return players.Contains(item);
}
public void CopyTo(Player[] array, int arrayIndex)
{
// throw new NotImplementedException();
players.CopyTo(array, arrayIndex);
}
public bool Remove(Player item)
{
return players.Remove(item);
}
// can go on to add additional methods
public Player Find(Predicate<Player> match) // this predicate returns bool value
{
return players.Find(match); //invokes the delegate object for each element in the list and returns first matching
}
public List<Player> FindAll(Predicate<Player> match) //return type is list of customer
{
return players.FindAll(match);
}
public int IndexOf(Player item) //meant for searhcing of customer index
{
return players.IndexOf(item);
}
public void Insert(int index, Player item)
{
players.Insert(index, item); // could add custom logic here
}
public void RemoveAt(int index)
{
if (index < 0)
{
Console.WriteLine("Invalid index");
}
else
{
players.RemoveAt(index);
}
}
}
class Program
{
static void Main()
{
PlayerList playerList = new PlayerList();
playerList.Add(new Player() { Id = "7", Name = "killmr", TypePlayer = TypeOfPlayer.RegularPlayer }); // note the use of our custom method inside custom collection class
playerList.Add(new Player() { Id = "9", Name = "NOE Grimes", TypePlayer = TypeOfPlayer.PayingPlayer });
//we now can add lots more dynamically aka object intitializer
playerList.Add(new Player() { Id = "1", Name = "edcat08", TypePlayer = TypeOfPlayer.PayingPlayer });
playerList.Add(new Player() { Id = "2", Name = "vmax", TypePlayer = TypeOfPlayer.PayingPlayer });
playerList.Add(new Player() { Id = "3", Name = "friki", TypePlayer = TypeOfPlayer.PayingPlayer });
Player new_player = new Player() { Id = "10", Name = "Jacob", TypePlayer = TypeOfPlayer.PayingPlayer };
playerList.Add(new_player);
//Contains
Console.WriteLine("Contains: " + playerList.Contains(new_player));
Console.WriteLine(playerList.Count + " customers found.");
//Remove
playerList.Remove(new_player);
foreach (Player player in playerList)
{
Console.WriteLine(player.Id + ", " + player.Name + ", " + player.TypePlayer);
}
////Find
Player matchingPlayer = playerList.Find(x => x.Id == "1");
if (matchingPlayer != null)
{
Console.WriteLine("Matching Player Found: " + matchingPlayer.Name + ", " + matchingPlayer.Id);
}
////FindAll
List<Player> paying_players = playerList.FindAll(player => player.TypePlayer == TypeOfPlayer.PayingPlayer);
Console.WriteLine("Paying players:");
foreach (Player player in paying_players)
{
Console.WriteLine(player.Id + ", " + player.Name + ", " + player.TypePlayer);
}
//IndexOf
Console.WriteLine("Index of: "+ playerList.IndexOf(new_player));
//Insert
playerList.Insert(2, matchingPlayer);
Console.WriteLine("Inserted: " + playerList[2].Name);
Console.WriteLine("Now will remove: " + playerList[3].Name);
playerList.RemoveAt(3);
//playerList.RemoveAt(4);
int i = 0;
foreach (Player item in playerList) // no need to call enumerator as this method is built into
{
Console.WriteLine("Modified playerlist:"+(i++)+" "+item.Name);
}
Console.ReadKey();
playerList.Clear();
}
}
}
//IDictionary implementation
using System;
using System.Collections.Generic;
using System.Collections;
namespace CustomCollectionIDictionary
{
public enum TypeOfPlayer
{
RegularPlayer, PayingPlayer
}
public class Player
{
public string? Id { get; set; }
public string? Name { get; set; }
public TypeOfPlayer TypePlayer { get; set; }
}
//custom collection generic class ICollection
class PlayerList : IDictionary<Player> //Needs to implement all methods in ICollection
{
private List<Player> players = new List<Player>();
public int Count => players.Count; // count of items present in collection
public bool IsReadOnly => false; // we want users to be able to modify this collection returns either true or false if found
public Player this[int index] { get => players[index]; set => players[index] = value; } //indexer with get /set accesor
//implementing IEnumerable.GetEnumerator()
//this method will get called first as its the direct implementaion of IEnumerable interface
//IEnumerator GetEnumerator() // explicit interface implementation by default this has to be private
//{
// return this.GetEnumerator();//from here we call the below method return type is IEnumerator
//}
//implementing IEnumerable<T> get enumerator
public IEnumerator<Player> GetEnumerator() // The problem with this is it doesnt implement defined methods like Add(),Find()
{
for (int i = 0; i < players.Count; i++)
{
yield return players[i];
}
}
//could manipultate more with Add(), Find(), Remove()
//lets add a user defined method
public void Add(Player player)
{
//custom Add logic
if (player.TypePlayer == TypeOfPlayer.PayingPlayer)
{
players.Add(player); // simply add to the list
}
else
{
Console.WriteLine("Sorry only paying players allowed");
}
}
public void Clear()
{
// throw new NotImplementedException();
}
public bool Contains(Player item)
{
return players.Contains(item);
}
public void CopyTo(Player[] array, int arrayIndex)
{
// throw new NotImplementedException();
players.CopyTo(array, arrayIndex);
}
public bool Remove(Player item)
{
return players.Remove(item);
}
// can go on to add additional methods
public Player Find(Predicate<Player> match) // this predicate returns bool value
{
return players.Find(match); //invokes the delegate object for each element in the list and returns first matching
}
public List<Player> FindAll(Predicate<Player> match) //return type is list of customer
{
return players.FindAll(match);
}
public int IndexOf(Player item) //meant for searhcing of customer index
{
return players.IndexOf(item);
}
public void Insert(int index, Player item)
{
players.Insert(index, item); // could add custom logic here
}
public void RemoveAt(int index)
{
if (index < 0)
{
Console.WriteLine("Invalid index");
}
else
{
players.RemoveAt(index);
}
}
}
class Program
{
static void Main()
{
PlayerList playerList = new PlayerList();
playerList.Add(new Player() { Id = "7", Name = "killmr", TypePlayer = TypeOfPlayer.RegularPlayer }); // note the use of our custom method inside custom collection class
playerList.Add(new Player() { Id = "9", Name = "NOE Grimes", TypePlayer = TypeOfPlayer.PayingPlayer });
//we now can add lots more dynamically aka object intitializer
playerList.Add(new Player() { Id = "1", Name = "edcat08", TypePlayer = TypeOfPlayer.PayingPlayer });
playerList.Add(new Player() { Id = "2", Name = "vmax", TypePlayer = TypeOfPlayer.PayingPlayer });
playerList.Add(new Player() { Id = "3", Name = "friki", TypePlayer = TypeOfPlayer.PayingPlayer });
Player new_player = new Player() { Id = "10", Name = "Jacob", TypePlayer = TypeOfPlayer.PayingPlayer };
playerList.Add(new_player);
//Contains
Console.WriteLine("Contains: " + playerList.Contains(new_player));
Console.WriteLine(playerList.Count + " customers found.");
//Remove
playerList.Remove(new_player);
foreach (Player player in playerList)
{
Console.WriteLine(player.Id + ", " + player.Name + ", " + player.TypePlayer);
}
////Find
Player matchingPlayer = playerList.Find(x => x.Id == "1");
if (matchingPlayer != null)
{
Console.WriteLine("Matching Player Found: " + matchingPlayer.Name + ", " + matchingPlayer.Id);
}
////FindAll
List<Player> paying_players = playerList.FindAll(player => player.TypePlayer == TypeOfPlayer.PayingPlayer);
Console.WriteLine("Paying players:");
foreach (Player player in paying_players)
{
Console.WriteLine(player.Id + ", " + player.Name + ", " + player.TypePlayer);
}
//IndexOf
Console.WriteLine("Index of: " + playerList.IndexOf(new_player));
//Insert
playerList.Insert(2, matchingPlayer);
Console.WriteLine("Inserted: " + playerList[2].Name);
Console.WriteLine("Now will remove: " + playerList[3].Name);
playerList.RemoveAt(3);
//playerList.RemoveAt(4);
int i = 0;
foreach (Player item in playerList) // no need to call enumerator as this method is built into
{
Console.WriteLine("Modified playerlist:" + (i++) + " " + item.Name);
}
Console.ReadKey();
playerList.Clear();
}
}
}
The System.IEquatable interface has a method called "Equals", which determines whether the current object and parameter object are logically equalor not, by comparing data of fields.
- It can be implemented in the class to make the objects comparable.
- It is useful to invoke List.Contains method to check whether the object is present in the collection or not
- this is good for checking the value of the objects
(see pdf)[_resources/IEquatable+Cheatsheet.pdf]
You can select lines of code and right click add break point then use f11 to cycle through certain parts of code Breakpoints:You set breakpoints f9 wherever you want to pause debugger execution. When you debug, execution pauses at the breakpoint, before the code on that line is executed. The breakpoint symbol shows a yellow arrow. F11 (Debug > Depurar paso a paso por instrucciones)
public class ClassName : IEquatable<ClassName>
{
public bool Equals(ClassName other)
{
return this.field1 == other.field1 && this.field2 == other.field2;
}
}
using System;
using System.Collections.Generic;
using System.Collections;
namespace CustomCollectionIEquatble
{
public enum TypeOfPlayer
{
RegularPlayer, PayingPlayer
}
public class Player : IEquatable<Player>
{
public string? Id { get; set; }
public string? Name { get; set; }
public TypeOfPlayer TypePlayer { get; set; }
public bool Equals(Player other) // here we implement IEquatable equals returns bool runs by default
{
Console.WriteLine("Equals running");
return this.Id == other.Id && this.Name == other.Name && this.TypePlayer == other.TypePlayer; //refers to first,second,.. element in the list if atleast one is found it will return true
}
}
//custom collection generic class IList
class PlayerList : IList<Player> //Needs to implement all methods in IList
{
private List<Player> players = new List<Player>();
public int Count => players.Count; // count of items present in collection
public bool IsReadOnly => false; // we want users to be able to modify this collection returns either true or false if found
public Player this[int index] { get => players[index]; set => players[index] = value; } //indexer with get /set accesor
//implementing IEnumerable.GetEnumerator()
//this method will get called first as its the direct implementaion of IEnumerable interface
//implementing IEnumerable.GetEnumerator()
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
//implementing IEnumerable<T> get enumerator
public IEnumerator<Player> GetEnumerator() // The problem with this is it doesnt implement defined methods like Add(),Find()
{
for (int i = 0; i < players.Count; i++)
{
yield return players[i];
}
}
//could manipultate more with Add(), Find(), Remove()
//lets add a user defined method
public void Add(Player player)
{
//custom Add logic
if (player.TypePlayer == TypeOfPlayer.PayingPlayer)
{
players.Add(player); // simply add to the list
}
else
{
Console.WriteLine("Sorry only paying players allowed");
}
}
public void Clear()
{
// throw new NotImplementedException();
}
public bool Contains(Player item)
{
return players.Contains(item); //can identify same object but this will cause issue when creating the same object twice
}
public void CopyTo(Player[] array, int arrayIndex)
{
// throw new NotImplementedException();
players.CopyTo(array, arrayIndex);
}
public bool Remove(Player item)
{
return players.Remove(item);
}
// can go on to add additional methods
public Player Find(Predicate<Player> match) // this predicate returns bool value
{
return players.Find(match); //invokes the delegate object for each element in the list and returns first matching
}
public List<Player> FindAll(Predicate<Player> match) //return type is list of customer
{
return players.FindAll(match);
}
public int IndexOf(Player item) //meant for searhcing of customer index
{
return players.IndexOf(item);
}
public void Insert(int index, Player item)
{
players.Insert(index, item); // could add custom logic here
}
public void RemoveAt(int index)
{
if (index < 0)
{
Console.WriteLine("Invalid index");
}
else
{
players.RemoveAt(index);
}
}
}
class Program
{
static void Main()
{
PlayerList playerList = new PlayerList();
playerList.Add(new Player() { Id = "7", Name = "killmr", TypePlayer = TypeOfPlayer.RegularPlayer }); // note the use of our custom method inside custom collection class
playerList.Add(new Player() { Id = "9", Name = "NOE Grimes", TypePlayer = TypeOfPlayer.PayingPlayer });
//we now can add lots more dynamically aka object intitializer
playerList.Add(new Player() { Id = "1", Name = "edcat08", TypePlayer = TypeOfPlayer.PayingPlayer });
playerList.Add(new Player() { Id = "2", Name = "vmax", TypePlayer = TypeOfPlayer.PayingPlayer });
playerList.Add(new Player() { Id = "3", Name = "friki", TypePlayer = TypeOfPlayer.PayingPlayer });
Player new_player = new Player() { Id = "12", Name = "Jacob", TypePlayer = TypeOfPlayer.PayingPlayer };
Player another_player = new Player() { Id = "12", Name = "Jacob", TypePlayer = TypeOfPlayer.PayingPlayer };
playerList.Add(new_player);
//Contains
Console.WriteLine("Contains: " + playerList.Contains(new_player)); //returns true
Console.WriteLine("Contains: " + playerList.Contains(another_player)); //returns true
//Remove
playerList.Remove(new_player);
foreach (Player player in playerList)
{
Console.WriteLine(player.Id + ", " + player.Name + ", " + player.TypePlayer);
}
////Find
Player matchingPlayer = playerList.Find(x => x.Id == "1");
if (matchingPlayer != null)
{
Console.WriteLine("Matching Player Found: " + matchingPlayer.Name + ", " + matchingPlayer.Id);
}
////FindAll
List<Player> paying_players = playerList.FindAll(player => player.TypePlayer == TypeOfPlayer.PayingPlayer);
Console.WriteLine("Paying players:");
foreach (Player player in paying_players)
{
Console.WriteLine(player.Id + ", " + player.Name + ", " + player.TypePlayer);
}
//IndexOf
Console.WriteLine("Index of: " + playerList.IndexOf(new_player));
//Insert
playerList.Insert(2, matchingPlayer);
Console.WriteLine("Inserted: " + playerList[2].Name);
Console.WriteLine("Now will remove: " + playerList[3].Name);
playerList.RemoveAt(3);
//playerList.RemoveAt(4);
int i = 0;
foreach (Player item in playerList) // no need to call enumerator as this method is built into
{
Console.WriteLine("Modified playerlist:" + (i++) + " " + item.Name);
}
Console.ReadKey();
playerList.Clear();
}
}
}
The System.IComparable interface has a method called "CompareTo", which determines order of two objects i.e. current object and parameter object. βΊ It can be implemented in the class to make the objects sortable. βΊ It is useful to invoke List.Sort method to sort the collection of objects.
public interface IComparable
{
int CompareTo(object obj);
}
> Record:A record type in C# 9 is a lightweight, immutable data type (or a lightweight class) that has read-only properties only.
using System;
using System.Collections.Generic;
namespace IComparable
{
public enum TypeOfPlayer
{
RegularPlayer, PayingPlayer
}
class Player : IComparable<Player>
{
public int? Id { get; set; }
public string? Name { get; set; }
public int? Rank { get; set; }
public int CompareTo(Player other)
{
// returns int whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object.
if (this.Rank < other.Rank)
{
return 1;
}
else if (this.Rank > other.Rank)
{
return -1;
}
else
{
return 0;
}
}
}
class Program
{
static void Main()
{
var players = new List<Player>
{
new Player(){Id=1,Name="friki",Rank=66},
new Player(){Id=2,Name="vmax",Rank=400},
new Player(){Id=3,Name="edcat08",Rank=440},
new Player(){Id=4,Name="Noe GRIMES",Rank=66},
new Player(){Id=5,Name="killmr",Rank=300},
new Player(){Id=5,Name="hacker",Rank=650},
};
Console.WriteLine("Comparing Player Ranks: ");
players.Sort();
players.ForEach(player => Console.WriteLine(player.Name));
Console.WriteLine("---------------------------");
players.Reverse();
players.ForEach(player => Console.WriteLine(player.Name));
}
}
}
The System.Collections.Generic.IComparer interface has a method called "Compare", which determines order of two objects i.e. current object and parameter object. βΊ It can be implemented by a separate class to make the objects sortable. βΊ It is useful to invoke List.Sort method to sort the collection of objects. βΊ It is an alternative to IComparable; useful for the classes that doesn't implement IComparable.
using System;
using System.Collections.Generic;
namespace IComparer
{
public enum TypeOfPlayer
{
RegularPlayer, PayingPlayer
}
class Player
{
public int Id { get; set; }
public string Name { get; set; }
public int Rank { get; set; }
}
class PlayerRank : IComparer<Player>
{
public int Compare(Player x, Player y) //returns int implements IComparer
{
int result = 0;
result = x.Rank - y.Rank;
return result;
}
}
class Program
{
static void Main()
{
var players = new List<Player>
{
new Player(){Id=1,Name="friki",Rank=66},
new Player(){Id=2,Name="vmax",Rank=400},
new Player(){Id=3,Name="edcat08",Rank=440},
new Player(){Id=4,Name="Noe GRIMES",Rank=66},
new Player(){Id=5,Name="killmr",Rank=300},
new Player(){Id=6,Name="hacker",Rank=650}
};
//PlayerRank ranker = new PlayerRank();
Console.WriteLine("Comparing Player Ranks: ");
players.Reverse();
players.ForEach(player => Console.WriteLine(player.Name + " Ranks: " + player.Rank));
Console.WriteLine("---------------------------");
PlayerRank ranker = new PlayerRank();
players.Sort(ranker); // Issue was that we needed to implement ! ArgumentException: At least one object must implement IComparable.
players.ForEach(player => Console.WriteLine(player.Name + " Ranks: " + player.Rank));
Console.WriteLine("---------------------------");
}
}
}
Here we have taken it one step further by defining a sort type. Not the use of compare to
using System.Collections.Generic;
namespace IComparer
{
class Player
{
public int Id { get; set; }
public string Name { get; set; }
public int Rank { get; set; }
}
public enum SortBy
{
Name,Id,Rank
}
class PlayerRank : IComparer<Player>
{
// public SortBy SortTypes { get; set; }
public SortBy sortTypes { get; set; }
public int Compare(Player x, Player y) //returns int implements IComparer
{
int result = 0;
switch (this.sortTypes)
{
case SortBy.Rank:
result = x.Rank - y.Rank;
break;
case SortBy.Name:
result = (x.Name !=null) ? x.Name.CompareTo(y.Name) : 0; // Implement IComparable CompareTo method - provide default sort order.
break;
case SortBy.Id:
result = x.Rank - y.Rank;
break;
}
return result;
}
}
class Program
{
static void Main()
{
var players = new List<Player>
{
new Player(){Id=1,Name="friki",Rank=66},
new Player(){Id=2,Name="vmax",Rank=400},
new Player(){Id=3,Name="edcat08",Rank=440},
new Player(){Id=4,Name="Noe GRIMES",Rank=66},
new Player(){Id=5,Name="killmr",Rank=300},
new Player(){Id=6,Name="hacker",Rank=650}
};
//PlayerRank ranker = new PlayerRank();
Console.WriteLine("Comparing Player Ranks: ");
players.Reverse();
players.ForEach(player => Console.WriteLine(player.Name + " Ranks: " + player.Rank));
Console.WriteLine("---------------------------");
PlayerRank ranker = new PlayerRank();
ranker.sortTypes = SortBy.Name;
players.Sort(ranker); // Issue was that we needed to implement ! ArgumentException: At least one object must implement IComparable.
players.ForEach(player => Console.WriteLine(player.Name + " Ranks: " + player.Rank));
Console.WriteLine("---------------------------");
}
}
}
In C#, covariance and contravariance enable implicit reference conversion for array types, delegate types, and generic type arguments. Covariance preserves assignment compatibility and contravariance reverses it.
The following code demonstrates the difference between assignment compatibility, covariance, and contravariance.
Covariance allows you to supply child type, where the parent type is expected. βΊ Implemented with "out" keyword for the generate type parameter of an interface. βΊ In this case, the generic type parameter can be used for return types of methods or properties created in the interface.
covariance = supply the child type name where the parent is expected
(see pdf)[Covariance+and+Contravariance+Cheatsheet.pdf]
InterfaceName<ParentType> variable = new ClassName<ChildType>( );
using System;
using System.Collections.Generic;
namespace Covariance
{
class LivingThing {
public int NumberOfLegs { get; set; }
}
class Parrot: LivingThing
{
}
class Dog: LivingThing
{
}
//out: for covariance says use this generic type t only as a return type cant be argument
//in for contravariance
interface IMover<out T> {
T Move();
}
// can call only methods of the same interface but not of child classes
public class Mover<T>:IMover<T>{
// T Method1(); // we can use this t as written type only but not as a parameter type
public T thing { get; set; }
public T Move()
{
return thing;
}
//T Method1(T x); //Invalid variance: The type parameter 'T' must be contravariantly valid on 'IMover<T>.Method1(T)'.
}
class Sample
{
public void PrintValues(IEnumerable<object> values) // IEnumerable<out T>
{
//List<object>values will not work is not covariance compliance T
foreach (var item in values)
{
Console.Write(item + ", ");
}
Console.WriteLine();
}
}
class Program
{
static void Main(string[] args)
{
LivingThing livingThing = new Parrot(); // not covariance
Parrot parrot = new Parrot(){ NumberOfLegs = 4}; //normal
//IMover<Parrot> mover = new Mover<Parrot>;
//Cannot implicitly convert type 'type1' to 'type2'. An explicit conversion exists (are you missing a cast?)
//IMover<LivingThing> mover = new Mover<Parrot>(); //this is not allowed in generic type parameters
//covariance = supply the child type name where the parent is expected
IMover<LivingThing> mover = new Mover<Parrot>(){ thing = parrot}; // here we can pass the parrot child object (this is required for geneirc)
//mover.Move(); //returns parrot objectg
Console.WriteLine("Moving with " + mover.Move().NumberOfLegs + " legs"); //"LivingThing" vs "Parrot"; supplying the child type (Parrot), where the parent type (LivingThing) is expected
//Covariance in real life
Sample s = new Sample();
s.PrintValues(new List<string>() { "hello", "world" });
Console.ReadKey();
}
}
}
Contravariance allows you to supply parent type, where the child type is expected. βΊ Implemented with "in" keyword for the generate type parameter of an interface. βΊ In this case, the generic type parameter can be used for parameter types of methods created in the interface.
supplying the parent type , where the child type is expected.
InterfaceName<ChildType> variable = new ClassName<ParentType>( );
CTRL ALt L Brings up solution Explorer Found this task difficult need to investigate: Collections,. List and interfaces. Had diffulty passing list object data back to the class
Create a C# app that helps teachers to create tests with MCQ (Multiple Choice Questions) and then students can answer those questions and also the system find outs marks secured of the student.
Process flow:
Step 1: First, system should accept two test papers; each test paper contains 10 MCQ.
Each question has four options; one question has only one correct answer (correct option).
Step 2: System should allow one student to attempt both the test papers. While student answers each question, it should display the question text with list of options.
Example:
What is the capital of U.S.A.
A) New York
B) Washington D.C.
C) London
D) New Delhi
It should accept student's choice. Ex: B
Step 3: System should automatically compare the correct answer with student's choice; find out marks secured of each question.
It should display all questions respective correct answer, student's choice, marks secured and finally total marks secured for test paper.
Overall, one student should answer two test papers.
Code instructions:
Create TestPaper, Question, Option, Student classes. Each class should implement respective interface. The interface should define the properties needed.
class TestPaper: Represents a test with a set of questions.
class Question: Represents a single question with a set of options and also stores the student's choice along with marks secured by the student.
class Option: Represents an option of a question. The option letter should be auto-generated as "A", "B" etc.
class Student: Represents a student along with the test papers attempted by him.
interface ITestPaper
string SubjectName { get; set; }
string TestPaperName { get; set; }
List<IQuestion> Questions { get; set; }
interface IQuestion
string QuestionText { get; set; }
List<IOption> Options { get; set; }
char CorrectAnswerLetter { get; set; }
char OptionSelectedByStudent { get; set; }
int MarksSecured { get; set; }
interface IOption
char OptionLetter { get; set; }
string OptionText { get; set; }
interface IStudent
int RollNo { get; set; }
string StudentName { get; set; }
List<ITestPaper> TestPapers { get; set; }
class Question
string QuestionText
List<IOption> Options
char CorrectAnswerLetter
char OptionSelectedByStudent
int MarksSecured
class Option
char OptionLetter
string OptionText
class Student
int RollNo
string StudentName
List<ITestPaper> TestPapers
Preguntas de esta tarea
Check your source code with instructor's solution.
*/
//using Entities.Abstract;
//using Entities.Concrete;
using System;
using System.Linq;
using TestPapers;
namespace MCQ
{
internal class Program
{
static void Main(string[] args)
{
TestPaper test1 = new TestPaper();
TestPaper test2 = new TestPaper();
var student = new Student { RollNo = 10, StudentName = "Joey", };
MakeTests(test1, test2);
Console.WriteLine("Test 1 Starting");
MarkTest(test1);
MarkTest(test2);
Console.WriteLine(CalculateMarks(test1, test2));
}
public static void MakeTests(TestPaper test1, TestPaper test2)
{
test1 = new TestPaper();
test2 = new TestPaper();
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Enter for First Test, Question " + (i + 1));
Console.WriteLine("Question Text:");
Question question = new Question();
string questionText = Console.ReadLine();
for (int j = 0; j < 4; j++)
{
Console.WriteLine("Enter Option " + (j + 1));
string optionText = Console.ReadLine();
Console.WriteLine("Enter Option Letter");
char optionLetter = Console.ReadLine()[0];
question.Options.Add(new Option { OptionLetter = optionLetter, OptionText = optionText });
}
question.QuestionText = questionText;
Console.WriteLine("Enter Answer");
char correct = Console.ReadLine()[0];
question.CorrectAnswerLetter = correct;
Console.WriteLine("Enter Mark ");
int mark = int.Parse(Console.ReadLine());
question.MarksSecured = mark;
test1.Questions.Add(question);
}
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Enter for Test 2, Q " + (i + 1));
Console.WriteLine(" Question Text:");
Question question = new Question();
string questionText = Console.ReadLine();
for (int j = 0; j < 4; j++)
{
Console.WriteLine("Enter Option " + (j + 1));
string optionText = Console.ReadLine();
Console.WriteLine("Enter Option Letter");
char optionLetter = Console.ReadLine()[0];
question.Options.Add(new Option { OptionLetter = optionLetter, OptionText = optionText });
}
question.QuestionText = questionText;
Console.WriteLine("Enter Correct Answer");
char correct = Console.ReadLine()[0];
question.CorrectAnswerLetter = correct;
Console.WriteLine("Enter Mark for this Question");
int mark = int.Parse(Console.ReadLine());
question.MarksSecured = mark;
test2.Questions.Add(question);
}
}
public static void MarkTest(TestPaper test)
{
for (int i = 0; i < test.Questions.Count; i++)
{
var question = test.Questions[i];
Console.WriteLine(question.QuestionText);
foreach (var item in question.Options)
{
Console.WriteLine(item.OptionLetter + ")" + item.OptionText);
}
char studentAnswer = Console.ReadLine()[0];
question.OptionSelectedByStudent = studentAnswer;
}
}
public static int CalculateMarks(params TestPaper[] tests)
{
int studentScores = 0;
foreach (var test in tests)
{
foreach (var item in test.Questions)
{
if (item.CorrectAnswerLetter == item.OptionSelectedByStudent)
{
studentScores += item.MarksSecured;
}
}
}
return studentScores;
}
}
}
βΊ When you create an object with a set of properties along with values; automatically C# compiler creates a class (with a random name) with specified properties. It is called as 'anonymous type' or 'anonymous classes'.
βΊ Useful when you want to quickly create a class that contains a specific set of properties.
You must initialize values into properties
with same set of data in various methods use class
var referenceVariable = new { Property1 = value, Property2 = value, β¦ }; // you dont need to mention classname
class RandomClassName
{
public type Property1 { get; set; }
public type Property2 { get; set; }
}
using System;
using CallOfDuty;
namespace AnonymousTypes
{
class Program
{
static void Main()
{
//Create object of player class
Player pl = new Player();
//calll methods creates anonymous class by c# compiler
var pAnon = new { Name = pl.GetPlayerName(), Rank = pl.GetPlayerRank() }; //data types assigend automatically (defualt string)
Console.WriteLine(pAnon.Rank + pAnon.Name);
//calll methods creates anonymous class
var p = new { Rank = 108, Player = "Friki" };
// Rest the mouse pointer over v.Amount and v.Message in the following
// statement to verify that their inferred types are int and string.
Console.WriteLine(p.Rank + p.Player);
//Anonymous types typically are used in the select clause of a query expression to return a subset of the properties from each object in the source sequence. For more information about queries, see LINQ in C#.
var anonArray = new[] { new { name = "apple", diam = 4 }, new { name = "grape", diam = 1 } };
}
}
}
(see pdf)[_resources/Anonymous+Types+Cheatsheet.pdf]
You can nest an anonymous object into another.
βΊ Then two anonymous types will be created.
var pAnonNested = new { Name = pl.GetPlayerName(), Prop2 = new { Name = pl.GetPlayerName(), Rank = pl.GetPlayerRank() } }; //data types assigend automatically (defualt string)
Console.WriteLine(pAnonNested.Name + " Anon Nested "+ pAnonNested.Prop2.Name);
(see pdf)[_resources/Anonymous+Types+Cheatsheet.pdf]
You can create 'array of anonymous objects' or 'implicitly typed array' with group of anonymous objects. βΊ All objects must contain same set of properties
//Anonymous types typically are used in the select clause of a query expression to return a subset of the properties from each object in the source sequence. For more information about queries, see LINQ in C#.
var anonArray = new[] { new { Name = "Friki", Rank = 40 }, new { Name = "Vmax", Rank = 100 } }; //all objects must contain same set of properties
Console.WriteLine(anonArray[1].Name);
(see pdf)[_resources/Anonymous+Types+Cheatsheet.pdf]
Useful to return multiple values from a method (or) to pass multiple values to a method.
Tuple stores only a set of values (of any data type); but doesn't store property names. βΊ So you should access them as Item1, Item2 etc.; which doesn't make sense some times. βΊ Tuple supports up to 8 elements only by default. βΊ You can store more than 8 values by using nested tuples (tuple inside tuple). βΊ Tuples are mainly used to pass multiple values to a method as parameter; and also return multiple values from a method.
var referenceVariable = new Tuple<type1, type2, β¦>( ) { value1, value2, β¦ };
using System;
namespace Tuples
{
class Sample{
public Tuple<int, string, string> GetPlayerDetails() { //5 ways of accessign data: separate class, anonymous objects, out tpye param, tuple, value tuple
Tuple<int, string, string> player = new Tuple<int, string, string>(1, "Friki", "Mara"); // not recommended to create more than 7
return player;
}
}
class Program
{
static void Main(string[] args)
{
//var referenceVariable = new Tuple<string, int>() {"im a string", 3};
Sample s = new Sample();
Tuple<int, string, string> player = s.GetPlayerDetails();
Console.WriteLine(player.Item1);// this item property can be confusing
Console.WriteLine(player.Item2);
Console.WriteLine(player.Item3);
//Creating new tuple
var player2 = Tuple.Create(1, "Vmax", "Mara");
Console.WriteLine(player2.Item1);
Console.WriteLine(player2.Item2);
Console.WriteLine(player2.Item3);
// Max of 8
var ranks = Tuple.Create(1, 2, 3, 4, 5, 6, 7, 8);
Console.WriteLine(ranks.Item1);
}
}
}
(see pdf)[_resources/Tuples+Cheatsheet.pdf]
βΊ 'Value Tuples' are advancement to 'Tuple' class with simplified syntax. βΊ Introduced in C# 7.1. βΊ Supports unlimited values. βΊ You will access elements with real field names; instead of Item1, Item2 etc. βΊ Can be used as method parameters / return value; much like Tuple class.
Good if: you want to return multiple values at a time
Nuget is required for value tuples older pre v22017 versions
- NuGet (pronounced "New Get") is a package manager designed to enable developers to share reusable code. Needed for creating, publishing, and consuming packages.
using System;
namespace Tuples
{
class CallOfDuty{
//value tuple
//good for returning multiple values at a time
public (int NumPlayers, string Squad) GetAllSquadDetails()
{ // change return type to
return (20, "Los Techeros");
}
}
class Program
{
static void Main(string[] args)
{
CallOfDuty cod = new CallOfDuty();
//Return value tuples (not readonly)
(int NumPlayers, string Squad) squad = cod.GetAllSquadDetails();
Console.WriteLine("Squad Count: "+ squad.NumPlayers + "Squad Name:" + squad.Squad);
}
}
}
(see pdf)[_resources/Tuples+Cheatsheet.pdf]
Deconstruction allows you to assign elements of a value tuple into individual local variables
(see pdf)[_resources/Tuples+Cheatsheet.pdf]
//Deconstruction
(int NumPlayers, string Squad) = cod.GetAllSquadDetails(); //note: removed reference var (dont change the order)
Console.WriteLine("Squad Count: " + NumPlayers + "Squad Name:" + Squad);
Discard allows you to skip a value which you don't require, by using underscore ( _ ).
This will work when deconstructing
//Deconstruction with Discard
(int NumPlayers,_) = cod.GetAllSquadDetails(); //note: removed reference var (dont change the order)
Console.WriteLine("Squad Count: " + NumPlayers + "Squad Name:" );
(see pdf)[_resources/Tuples+Cheatsheet.pdf]
When the various components in a system are organized systematically we call it a system architecture. The architecture is the enterprise-scale division of a system into layers or tiers, each having responsibility for a major part of the system and with as little direct influence on other layers.
Start by adding new project adding new class libaries:
framework must be same as other library
JoeBank.Entities JoeBank.DataAccesLayer JoeBank.BusinessLogicLayer JoeBank.Presentation
Lets add two new libraries: Exceptions,Config
Entities: we want to use in all the project and all the solutions
Summary block Type in /// before your method, property, etc. VS will generate the comment blocks automatically.
You can right click on a propery and hit encapsulate field this will generateteh initializer below
In tools there is an option for guid creation
/// <summary>
/// Represents custom of a the bank
/// public and implements ICustomer
/// </summary>
#region Private fields
#endregion
(See CS Project Exceptions)[C:\www\c-sharp-master-class\JoeBank\JoeBank.Exceptions\CustomerException.cs] (See CS Project Validations)[C:\www\c-sharp-master-class\JoeBank\JoeBank.Entities\Customer.cs]
We have no db conx Requires interface benefit of layers means teams can work on differnt segments > You can add an interface via right click in your *DAL Folder* and
(See CS Project Customers in Dal)[C:\www\c-sharp-master-class\JoeBank\JoeBank.DataAccesLayer\CustomersDataAccessLayer.cs] (See CS Project ICustomers in Dal)[C:\www\c-sharp-master-class\JoeBank\JoeBank.DataAccesLayer\DALContracts\ICustomersDataAccessLayer.cs]
(See CS Project Customers in Dal)[C:\www\c-sharp-master-class\JoeBank\JoeBank.DataAccesLayer\CustomersDataAccessLayer.cs]
Db creds could go here for example For this we will create a static class & properties (See CS Project Settings)[C:\www\c-sharp-master-class\JoeBank\JoeBank.Configuration\Settings.cs]
This is for the customer entity. Its a class that contains the logic of decisions to make before doing somethign
- Should not interact with the data source directly
- bll should call dal
- never interact with user directly
- or read any inputs No Console.
- Mediator between the data access layer and the presentation layer
- Most of methods between bll / dal will be the same
- Best to add comments and exceptions from beginging
- accepts inputs from user
- provides output
- no validation happens here
- this happens in bll
- presentation always call bll
- bll invokes data access layer
- catch all exceptions here top most layer
In realworld apps you can use the (password text boxes)[https://docs.microsoft.com/en-us/dotnet/desktop/winforms/controls/how-to-create-a-password-text-box-with-the-windows-forms-textbox-control?view=netframeworkdesktop-4.8] to hide the password
[x] Customers: Edit Customer, Delete Customer [x] Accounts: New Account, Edit Account, View Accounts, Delete Account [x] Funds Transfer: Transfer amount from one bank account to another bank account [x] Funds Transfer Statement: View list of funds transfers between dates [x] Account Statement: View list of debit and credit transactions between specified dates
Implement above mentioned operations by using all the code standards and best practices shown in the previous lectures.
Need to validate: Transfer Account Exist Transfer top down:
transferBll.ApplyTransfer()
Transfer Object built from ( SourceAccount, TargetAccount, Transfer(){TransferAmount,TransferDate} )
acc.BLLApplyTransfer()
-> accBLL.SubmitTransaction(Account account, Transaction transaction)
takes source account and transfer object (could call add transaction directly)
//initits new accBll, swaps transaction amounts, //Does validate transfer here
SubmitTransaction
-> sets this.AddTransaction(Account.Transfer)
(which invokes _accountDAL
dal)
//Does logic to check type, check against balance and update balance
AddTransaction(account,transaction)
calls found account and add transaction to the list
Language integrated query. Write one query
- Contains foreach internally
- lambda expression See Check method above (lambda acts like mehtods)
using System;
using System.Collections.Generic;
using System.Linq; //required
namespace LINQExample
{
class Employee
{
public int EmpID { get; set; }
public string EmpName { get; set; }
public string Job { get; set; }
public string City { get; set; }
public double Salary { get; set; }
}
class Person
{
public string PersonName { get; set; }
}
class Program
{
static void Main(string[] args)
{
//Collection of objects
List<Employee> employees = new List<Employee>() { // create new list of type <Employee>
new Employee{ EmpID = 12,EmpName ="Joey", Job="Developer", City = "Malaga", Salary = 79030 }, //Initialize new objects
new Employee{ EmpID = 22,EmpName ="Bill", Job="Analyst", City = "Madrid", Salary = 79031 },
new Employee{ EmpID = 3,EmpName ="Steve", Job="Developer", City = "Zaragoza", Salary = 14200 },
new Employee{ EmpID = 44,EmpName ="Winnie",Job="Designer", City = "London", Salary = 64020 },
new Employee{ EmpID = 10,EmpName ="Carlos",Job="Manager", City = "Zaragoza", Salary = 40000 }
};
//Where
// var results = employees.Where(emp => emp.Job == "Developer");
IEnumerable<Employee> results = employees.Where(emp => emp.City == "Zaragoza");
//where ever there is IEnumerable you can perform a foreach
foreach (Employee emp in results)
{
Console.WriteLine("Results Where: "+ emp.EmpName + ", " + emp.City);
}
//Orderby
IOrderedEnumerable<Employee> orderdByName = employees.OrderBy(emp => emp.EmpName); // Lets use (same as IEnumerable) IOrderedEnumerable its used to add a secondary sort criteria represents well sorted data
IOrderedEnumerable<Employee> orderdByNameDescending = employees.OrderByDescending(emp => emp.EmpName); // descending
IOrderedEnumerable<Employee> orderdByJobThenName = employees.OrderBy(emp => emp.Job).ThenBy(emp => emp.EmpName); // two conditions secondary sorting property using ThenBy() ThenByDescending()
foreach (Employee emp in orderdByJobThenName)
{
Console.WriteLine("Results Ordered by job and name: " + emp.EmpName + ", " + emp.Job + ", " + emp.City);
}
//To list
// IEnumerable<Employee> filteredEmployees = employees.Where(emp => emp.Job == "Developer").FirstOrDefault(); // where returns muliple value
List<Employee> filteredEmployeesList = employees.Where(emp => emp.Job == "Developer").ToList(); // need to convert using ToList() working with lists where returns IEnumberable
Console.WriteLine("To List:" + filteredEmployeesList[0].EmpName);
//First
Employee filteredEmployeesFirst = employees.First(emp => emp.Job == "Developer");
Console.WriteLine("First:" + filteredEmployeesFirst.EmpName); // no need for indexer
//First or default
//When no matching is found using First emp.Job == "Sweeper"
Employee filteredEmployeesFirstOrDefault = employees.FirstOrDefault(emp => emp.Job == "Sweeper"); //returns null rather than invalid
//using null here
if (filteredEmployeesFirstOrDefault != null)
{
Console.WriteLine("First Or default " + filteredEmployeesFirstOrDefault.EmpName); // no need for indexer
}
else {
Console.WriteLine("No Employee found for query"); // no need for indexer
}
//Last
Employee filteredEmployeesLast = employees.Last(emp => emp.Job == "Developer");
Console.WriteLine("Last:" + filteredEmployeesLast.EmpName); // no need for indexer
//Last or default
//When no matching is found using Last emp.Job == "Sweeper"
Employee filteredEmployeesLastOrDefault = employees.LastOrDefault(emp => emp.Job == "Sweeper"); //returns null rather than invalid
//using null here
if (filteredEmployeesLastOrDefault != null)
{
Console.WriteLine("Last Or default " + filteredEmployeesLastOrDefault.EmpName); // no need for indexer
}
else
{
Console.WriteLine("No Employee found for query");
}
//ElementAt
Employee filteredEmployeesElementAt = employees.ElementAt(3);
Console.WriteLine("ElementAt index:" + filteredEmployeesElementAt.EmpName);
//ElementAt or default
//When no matching is found using ElementAt emp.Job == "Sweeper"
Employee filteredEmployeesElementAtOrDefault = employees.ElementAtOrDefault(100); //returns null rather than invalid
//using null here
if (filteredEmployeesElementAtOrDefault != null)
{
Console.WriteLine("ElementAt Or default " + filteredEmployeesElementAtOrDefault.EmpName); // no need for indexer
}
else
{
Console.WriteLine("No Employee found for query"); // no need for indexer
}
//Single
//(Expects only one matching element works well with unique properties)
//The difference here is that if the result is multiple it returns unhandled exception
Employee filteredEmployeesSingle = employees.Single(emp => emp.Job == "Designer"); //not IEnumerable
Console.WriteLine("Single:" + filteredEmployeesSingle.EmpName); // no need for indexer
//Single or default
Employee filteredEmployeesSingleOrDefault = employees.SingleOrDefault(emp => emp.Job == "Analyst");
//using null here
if (filteredEmployeesSingleOrDefault != null)
{
Console.WriteLine("Single Or default " + filteredEmployeesSingleOrDefault.EmpName); // no need for indexer
}
else
{
Console.WriteLine("No Employee found for query"); // no need for indexer
}
//Select
//Selects only a bunch of cols/vals from source
//need to specify return type to match your value
//based on the type it will take up the value automatically
//good for converting types
IEnumerable<string> filteredEmployeesSelect = employees.Select(emp => emp.EmpName).ToList(); //can use any collection methods on these dictionay etc
Console.WriteLine("Select First Combo:" + filteredEmployeesSelect.First());
foreach (var item in filteredEmployeesSelect)
{
Console.WriteLine("Select: " + item); // no need for indexer
}
//Using select to object init
//IEnumerable<Person> filteredEmployeesSelectWithNewObjectInit = employees.Select(emp => new Person() { PersonName = emp.EmpName }); //produce new person object for each item in collection
List<Person> filteredEmployeesSelectWithNewObjectInit = employees.Select(emp => new Person() { PersonName = emp.EmpName }).ToList(); //to list can be usefull to access all of the list extension methods (add,addRange)
foreach (Person item in filteredEmployeesSelectWithNewObjectInit)
{
Console.WriteLine(item.PersonName);
}
// Min, Max, Count, Sum, Average
double maxItem = employees.Max(emp => emp.Salary);
Console.WriteLine("Max item:" + maxItem);
double minItem = employees.Min(emp => emp.Salary);
Console.WriteLine("Min item:" + minItem);
int count = employees.Count();
Console.WriteLine("Count:" + count);
double sum = employees.Sum(emp => emp.Salary);
Console.WriteLine("Sum of all items:" + sum);
double avg = employees.Average(emp => emp.Salary);
Console.WriteLine("Average Salary:" + avg);
}
}
}
-
Uses IEnumberable
-
Specify return type since all you are promising is "a sequence of (typed) values". Ideal for LINQ etc, and perfectly usable.
-
allows readonly access to a collection then a collection that implements IEnumerable can be used with a for-each statement.
-
when we want to iterate among our classes using a foreach loop.
-
If you do not counting in your external code it is always better to return IEnumerable
-
Outputs errors at runtime -Works better with intellisense
using System;
using System.Collections.Generic;
using System.Linq; //required
namespace LINQExample
{
class Employee {
public int EmpID { get; set; }
public string EmpName { get; set; }
public string Job { get; set; }
public string City { get; set; }
}
class Temp {
//method that takes employee as param returns bool
//works same as Linq where
public bool Check(Employee emp) {
return emp.Job == "Manager";
}
}
class Program
{
static void Main(string[] args)
{
//Collection of objects
List<Employee> employees = new List<Employee>() { // create new list of type <Employee>
new Employee{ EmpID = 1,EmpName ="Joey", Job="Developer", City = "Malaga" }, //Initialize new objects
new Employee{ EmpID = 2,EmpName ="Bill", Job="Analyst", City = "Madrid" },
new Employee{ EmpID = 3,EmpName ="Steve",Job="Developer", City = "Zaragoza" },
new Employee{ EmpID = 4,EmpName ="Winnie",Job="Designer", City = "London" },
new Employee{ EmpID = 4,EmpName ="Carlos",Job="Manager", City = "Zaragoza" }
};
// var results = employees.Where(emp => emp.Job == "Developer");
IEnumerable<Employee> results = employees.Where(emp => emp.City == "Barcelona");
//where ever there is IEnumerable you can perform a foreach
foreach (Employee emp in results)
{
Console.WriteLine("π¨βπΌ " + emp.EmpName + ", "+ emp.City);
}
var result = employees.Where(emp => emp.Job == "Developer").FirstOrDefault();
Console.WriteLine("π¨βπ» First or Default:" + result.EmpName);
}
}
}
~~~
<img src="_img/linq.png" >
<img src="_img/linq-retievals.png">
<img src="_img/linq-extension-methods.png">
---
## OrderBy
> Shift F5 is stop debugging
~~~
//Orderby
IOrderedEnumerable<Employee> orderdByName = employees.OrderBy(emp => emp.EmpName); // Lets use (same as IEnumerable) IOrderedEnumerable its used to add a secondary sort criteria represents well sorted data
IOrderedEnumerable<Employee> orderdByNameDescending = employees.OrderByDescending(emp => emp.EmpName); // descending
IOrderedEnumerable<Employee> orderdByJobThenName = employees.OrderBy(emp => emp.Job).ThenBy(emp => emp.EmpName); // two conditions secondary sorting property using ThenBy() ThenByDescending()
foreach (Employee emp in orderdByJobThenName)
{
Console.WriteLine("π¨βπΌ Results Ordered by job and name: " + emp.EmpName + ", " + emp.Job + ", " + emp.City);
}
~~~
(see pdf)[_resources/LINQ+Cheatsheet.pdf]
<img src="_img/linq-order-by.png" >
<img src="_img/linq-then-by-descending.png">
---
216.
## First and FirstOrDefault
~~~
//To list
// IEnumerable<Employee> filteredEmployees = employees.Where(emp => emp.Job == "Developer").FirstOrDefault(); // where returns muliple value
List<Employee> filteredEmployeesList = employees.Where(emp => emp.Job == "Developer").ToList(); // need to convert using ToList() working with lists where returns IEnumberable
Console.WriteLine("π¨βπ» To List:" + filteredEmployeesList[0].EmpName);
//First
Employee filteredEmployeesFirst = employees.First(emp => emp.Job == "Developer");
Console.WriteLine("π¨βπ» First:" + filteredEmployeesFirst.EmpName); // no need for indexer
//First or default
//When no matching is found using First emp.Job == "Sweeper"
Employee filteredEmployeesFirstOrDefault = employees.FirstOrDefault(emp => emp.Job == "Sweeper"); //returns null rather than invalid
//using null here
if (filteredEmployeesFirstOrDefault != null)
{
Console.WriteLine("π¨βπ» First Or default " + filteredEmployeesFirstOrDefault.EmpName); // no need for indexer
}
else {
Console.WriteLine("π¨βπ» No Employee found for query"); // no need for indexer
}
~~~
(see pdf)[_resources]
<img src="_img/linq-first.png" >
---
## Last and LastOrDefault
~~~
//Last
Employee filteredEmployeesLast = employees.Last(emp => emp.Job == "Developer");
Console.WriteLine("π¨βπ» Last:" + filteredEmployeesLast.EmpName); // no need for indexer
//Last or default
//When no matching is found using Last emp.Job == "Sweeper"
Employee filteredEmployeesLastOrDefault = employees.LastOrDefault(emp => emp.Job == "Sweeper"); //returns null rather than invalid
//using null here
if (filteredEmployeesLastOrDefault != null)
{
Console.WriteLine("π¨βπ» Last Or default " + filteredEmployeesLastOrDefault.EmpName); // no need for indexer
}
else
{
Console.WriteLine("π¨βπ» No Employee found for query"); // no need for indexer
}
~~~
(see pdf)[_resources]
<img src="_img/linq-last.png" >
---
## ElementAt and ElementAtOrDefault
~~~
//ElementAt
Employee filteredEmployeesElementAt = employees.ElementAt(3);
Console.WriteLine("π¨βπ» ElementAt index:" + filteredEmployeesElementAt.EmpName);
//ElementAt or default
//When no matching is found using ElementAt emp.Job == "Sweeper"
Employee filteredEmployeesElementAtOrDefault = employees.ElementAtOrDefault(100); //returns null rather than invalid
//using null here
if (filteredEmployeesElementAtOrDefault != null)
{
Console.WriteLine("π¨βπ» ElementAt Or default " + filteredEmployeesElementAtOrDefault.EmpName); // no need for indexer
}
else
{
Console.WriteLine("π¨βπ» No Employee found for query"); // no need for indexer
}
~~~
<img src="_img/linq-element-at.png" >
---
## Single and SingleOrDefault
Similar to First or FirstOrDefault
- element works well with unique properties
- The difference here is that if the result is multiple it returns unhandled exception
~~~
//Single
//(Expects only one matching element works well with unique properties)
//The difference here is that if the result is multiple it returns unhandled exception
Employee filteredEmployeesSingle = employees.Single(emp => emp.Job == "Designer"); //not IEnumerable
Console.WriteLine("π¨βπΌ Single:" + filteredEmployeesSingle.EmpName); // no need for indexer
//Single or default
Employee filteredEmployeesSingleOrDefault = employees.SingleOrDefault(emp => emp.Job == "Developer");
//using null here
if (filteredEmployeesSingleOrDefault != null)
{
Console.WriteLine("π¨βπΌ Single Or default " + filteredEmployeesSingleOrDefault.EmpName); // no need for indexer
}
else
{
Console.WriteLine("π¨βπΌ No Employee found for query"); // no need for indexer
}
~~~
<img src="_img/linq-single.png" >
---
## Select
<img src="_img/linq-select.png" >
- Generates new collection with new elements based on each existing element
- Selects only a bunch of cols/vals from source
- need to specify return type to match your value
- based on the type it will take up the value automatically
- good for converting types
~~~
//Select
//Selects only a bunch of cols/vals from source
//need to specify return type to match your value
//based on the type it will take up the value automatically
//good for converting types
IEnumerable<string> filteredEmployeesSelect = employees.Select(emp => emp.EmpName).ToList(); //can use any collection methods on these dictionay etc
Console.WriteLine("π¨βπΌ Select First Combo:" + filteredEmployeesSelect.First());
foreach (var item in filteredEmployeesSelect)
{
Console.WriteLine("π¨βπΌ Select: " + item); // no need for indexer
}
//Using select to object init
//IEnumerable<Person> filteredEmployeesSelectWithNewObjectInit = employees.Select(emp => new Person() { PersonName = emp.EmpName }); //produce new person object for each item in collection
List<Person> filteredEmployeesSelectWithNewObjectInit = employees.Select(emp => new Person() { PersonName = emp.EmpName }).ToList(); //to list can be usefull to access all of the list extension methods (add,addRange)
foreach (Person item in filteredEmployeesSelectWithNewObjectInit)
{
Console.WriteLine("π¨βπΌ " + item.PersonName);
}
~~~
---
## Min, Max, Count, Sum, Average
~~~
// Min, Max, Count, Sum, Average
double maxItem = employees.Max(emp => emp.Salary);
Console.WriteLine("π¨βπΌ Max item:" + maxItem);
double minItem = employees.Min(emp => emp.Salary);
Console.WriteLine("π¨βπΌ Min item:" + minItem);
int count = employees.Count();
Console.WriteLine("π¨βπΌ Count:" + count);
double sum = employees.Sum(emp => emp.Salary);
Console.WriteLine("π¨βπΌ Sum of all items:" + sum);
double avg = employees.Average(emp => emp.Salary);
Console.WriteLine("π¨βπΌ Average Salary:" + avg);
---
## String, Datetime, Math
### String π§΅
A collection of characters
For each string you create a new System.Stringobject will be created
~~~
namespace StringExample
{
class Program
{
static void Main()
{
string x = "Hello World"; //creates a string object
int n = x.Length; // count of characters
char c = x[0]; //character at specific index
Console.WriteLine(x);
Console.WriteLine(n);
Console.WriteLine(c);
Console.ReadKey();
}
}
}
~~~
(see pdf)[_resources/String+Cheatsheet.pdf]
<img src="_img/string.png" >
### How String Objects are Created π€
~~~
//String objects
string x1 = "HELLO WORLD";
string y1 = "HELLO WORLD";
string z1 = x1; //copies address from 'x' to 'z'
x1 = "hello"; //creates a new string object
Console.WriteLine(x1); //hello
Console.WriteLine(y1); //HELLO WORLD
Console.WriteLine(z1); //HELLO WORLD
Console.ReadKey();
~~~
### Converting Strings - Part 1 , Part 2, Part 3 π
[<i class="fas fa-file-pdf"></i>](resources/String+Cheatsheet.pdf)
[<i class="fas fa-file-pdf"></i>](resources)