Part
50 - C# Tutorial - Internal and Protected Internal Access Modifiers
Now, if we build the solution we should have 3 assemblies generated. Two dll's and one exe. To locate the physical assembly follow these steps.
1. Right click on AssemblyOne project, in solution explorer and select Open Folder in Windows Explorer.
2. Open bin folder
3. Now open Debug folder
4. In the Debug folder you should see AssemblyOne.dll, which is the physical assembly.
Copy and paste the following code in class1.cs file of AssemblyOne project using System;
namespace AssemblyOne
{
public class AssemblyOneClassI
{
internal int ID = 999;
}
public class AssemblyOneClassII
{
public void Test()
{
AssemblyOneClassI instance = new AssemblyOneClassI();
// Can access inetrnal member ID, AssemblyOneClassII and AssemblyOneClassI
// are present in the same assembly
Console.WriteLine(instance.ID);
}
}
}
In this example, AssemblyOneClassI has an internal member ID. We can access ID member from AssemblyOneClassII, because this class is also present in the same assembly as AssemblyOneClassI.
Now, Copy and Paste the following code, in Class1.cs of AssemblyTwo project.using System;
using AssemblyOne;
namespace AssemblyTwo
{
public class AssemblyTwoClassI
{
public void Test()
{
AssemblyOneClassI instance = new AssemblyOneClassI();
//Console.WriteLine(instance.ID);
}
}
}
Note: You will get 3 compiler errors at this point. To solve this we need to add an assembly reference. Follow these steps.
1. Expand References folder under AssemblyTwo project, from Solution Explorer.
2. Right Click on References folder and select Add Reference
3. From the Add Reference dialog box, select Projects tab
4. From the list, select AssemblyOne project and click OK.
At, this point all the compiler errors should have gone.
Uncomment the following line from Class1.cs file from AssemblyTwo project and rebuild the solution.
Console.WriteLine(instance.ID);
Now, you will get a compiler error stating 'AssemblyOne.AssemblyOneClassI' does not contain a definition for 'ID' and no extension method 'ID' accepting a first argument of type 'AssemblyOne.AssemblyOneClassI' could be found (are you missing a using directive or an assembly reference?).
This is because, AssemblyTwoClassI is not present in AssemblyOne assembly and hence cannot access the internal ID member defined in AssemblyOne assembly. This proves that internal members are only accessible with in the same assembly. Code outside of the containing assembly cannot access internal members.
Protected Internal:Protected Internal members can be accessed by any code in the assembly in which it is declared, or from within a derived class in another assembly. It is a combination of protected and internal. If you have understood protected and internal, this should be very easy to follow.
Now, change the access modifier from internal to protected internal for ID member in AssemblyOneClassI of class1.cs file in AssemblyOne project.
internal int ID = 999; to protected internal int ID = 999;
Finally modify the code in Class1.cs file in AssemblyTwo project as shown below.using System;
using AssemblyOne;
namespace AssemblyTwo
{
// Make AssemblyTwoClassI inherit from AssemblyOneClassI
public class AssemblyTwoClassI : AssemblyOneClassI
{
public void Test()
{
AssemblyOneClassI instance = new AssemblyOneClassI();
// Access the base class member using the base keyword
Console.WriteLine(base.ID);
}
}
}
So, this shows protected internal ID member, defined in AssemblyOne is accessible in AssemblyTwo.
Part
51 - C# Tutorial - Access Modifiers for types
Please
watch the following videos, before continuing with this part.
Part 48. Difference between Types and Type Members
Part 49. Private, Public and Protected access modifiers
Part 50. Internal and Protected Internal access modifiers
In c# there are 5 different access modifiers.
1. Private
2. Public
3. Protected
4. Internal
5. Protected Internal
Part 48. Difference between Types and Type Members
Part 49. Private, Public and Protected access modifiers
Part 50. Internal and Protected Internal access modifiers
In c# there are 5 different access modifiers.
1. Private
2. Public
3. Protected
4. Internal
5. Protected Internal
Part
51 - C# Tutorial - Access Modifiers for types
You can use all the 5 access modifiers with type members, but types allows only internal and public access modifiers. It is a compile time error to use private, protected and protected internal access modifiers with types.
The following code will generate a compiler error stating Elements defined in a namespace cannot be explicitly declared as private, protected, or protected internal
using System;
namespace Pragim
{
//Error: Cannot mark types with private, protected and protected internal access modifiers
private class MainClass
{
public static void Main()
{
Console.WriteLine("This code will not compile");
}
}
}
Add 2 class library projects to the solution with names AssemblyOne and AssemblyTwo. If you want to learn to do this, please check Part 50 - Access Modifiers - Internal and Protected Internal.
Copy and paste the following code in Class1.cs file of AssemblyOne project.
using System;
namespace AssemblyOne
{
//Class is marked internal. This class is available only with in AssemblyOne
internal class AssemblyOneClass
{
public void Print()
{
Console.WriteLine("Hello");
}
}
}
Copy and paste the following code in Class1.cs file of AssemblyTwo project.
using System;
using AssemblyOne;
namespace AssemblyTwo
{
//Class is marked public. This class is available in any assembly
public class AssemblyTwoClass
{
public void Print()
{
AssemblyOneClass instance = new AssemblyOneClass();
instance.Print();
}
}
}
Add a reference to AssemblyOne project, from AssemblyTwo project. Please check the previous session, to learn about adding project references.
Now build the solution. You will notice the following 4 compiler errors.
1. 'AssemblyOne.AssemblyOneClass' is inaccessible due to its protection level
2. The type 'AssemblyOne.AssemblyOneClass' has no constructors defined
3. 'AssemblyOne.AssemblyOneClass' is inaccessible due to its protection level
4. 'AssemblyOne.AssemblyOneClass' does not contain a definition for 'Print' and no extension method 'Print' accepting a first argument of type 'AssemblyOne.AssemblyOneClass' could be found (are you missing a using directive or an assembly reference?)
All these errors are in AssemblyTwo project, and are related to AssemblyOne.AssemblyOneClass being inaccessible due to its protection level.
Now convert the access modifier of AssemblyOneClass from internal to public and rebuild the solution. Now we get no errors. This shows that internal types are accessible only with in the containing assembly.
Now just remove the public access modifier from AssemblyOneClass and rebuild the solution. You now again get the same 4 errors that we got before. This is because, if you don't specify an access modifier for a type, then by default the access modifier will be internal.
So if you don't specify an access modifier, then for Types the default is internal and for type members it is private.
In this part we will learn
1. The purpose of attributes
2. Using an attribute
3. Customizing attribute using parameters
Purpose: Attributes allow you to add declarative information to your programs. This information can then be queried at runtime using reflection.
1. The purpose of attributes
2. Using an attribute
3. Customizing attribute using parameters
Purpose: Attributes allow you to add declarative information to your programs. This information can then be queried at runtime using reflection.
Part 52 - C# Tutorial - Attributes in C#
There are several Pre-defined Attributes provided by .NET. It is also possible to create your own Custom Attributes. Creating custom attributes is beyond the scope of this article.
A few pre-defined attributes with in the .NET framework.
Obsolete - Marks types and type members outdated
WebMethod - To expose a method as an XML Web service method
Serializable - Indicates that a class can be serialized
Example program using pre defined Obsolete attribute:
Obsolete attribute can be used with types or type members that are obsolete (Outdated). If a developer uses a type or a type member that is decorated with obsolete attribute, the compiler issues a warning or an error depending on how the attribute is configured.
In this sample program, Add(int FirstNumber, int SecondNumber) method is decorated with [Obsolete] attribute. If you compile this program, in the output window you will see a warning message (Compile complete -- 0 errors, 1 warnings). Also, visual studio, shows a green squiggly line under the Add(int FirstNumber, int SecondNumber) method. If you hover the mouse over the squiggly line, you should see the warning message.
Note: If you don't see the warning message (Compile complete -- 0 errors, 1 warnings), rebuild the soultion.
using System;
using System.Collections.Generic;
public class MainClass
{
private static void Main()
{
Calculator.Add(10, 15);
}
}
public class Calculator
{
[Obsolete]
public static int Add(int FirstNumber, int SecondNumber)
{
return FirstNumber + SecondNumber;
}
public static int Add(List<int> Numbers)
{
int Sum = 0;
foreach (int Number in Numbers)
{
Sum = Sum + Number;
}
return Sum;
}
}
The warning message says 'Calculator.Add(int, int)' is obsolete. However, this message is not completely useful, because it says 'Calculator.Add(int, int)' is obsolete, but not tell us which other method should we be using instead. So this is when we can customize, the warning message using attribute parameters.
The intention of the developer of Calculator class is that, he wanted us to use Add(List<int> Numbers), instead of int Add(int FirstNumber, int SecondNumber). To communicate this message we can customize the warning message using attribute parameters as shown below. With this customization we are not only communicating that Add(int FirstNumber, int SecondNumber) method is obsolete, we are also telling to use the alternative method that is available.
[Obsolete("Use Add(List<int> Numbers) instead")]
public static int Add(int FirstNumber, int SecondNumber)
If you want to generate a compiler error instead of warning, pass true for the bool error parameter of the Obsolete attribute as shown below. Now, we can't even compile the program.
[Obsolete("Use Add(List<int> Numbers) instead", true)]
public static int Add(int FirstNumber, int SecondNumber)
Finally, If you right click on Obsolete attribute and select Go To Definition, you will see that, an attribute is nothing but a class that inherits from System.Attribute base class.
Part 53 - C# Tutorial
- Reflection
Reflection
is the ability of inspecting an assemblie's metadata at runtime. It is
used to find all types in an assembly and/or dynamically invoke methods in an
assembly. This includes information about the type, properties, methods, and
events of an object. With Reflection, we can dynamically create an instance of
a type, bind the type to an existing object, or get the type from an existing
object and invoke its methods or access its fields and properties.There are
several uses of reflection.
Part 53 - C# Tutorial - Reflection
1. When you drag and drop a button on a win forms or an asp.net application. The properties window uses reflection to show all the properties of the Button class. So,reflection is extensivley used by IDE or a UI designers.
2. Late binding can be achieved by using reflection. You can use reflection to dynamically create an instance of a type, about which we don't have any information at compile time. So, reflection enables you to use code that is not available at compile time.
3. Consider an example where we have two alternate implementations of an interface. You want to allow the user to pick one or the other using a config file. With reflection, you can simply read the name of the class whose implementation you want to use from the config file, and instantiate an instance of that class. This is another example for late binding using reflection.
So, in short reflection can be used for type discovery (i.e finding methods, properties, events, fields, constructors etc) and late binding.
In this session we will learn how to list a specifc class methods, properties, fields etc using reflection. All the classes and methods related to reflection are present in System.Reflection namespace.
The Type class is the most importanct class.
Consider the Customer class example. This class has got
1. Two constructors
2. Two auto implemeneted properties
3. Two methods
using System;
using System.Reflection;
namespace Pragim
{
public class MainClass
{
private static void Main()
{
// Get the Type Using GetType() static method
Type T = Type.GetType("Pragim.Customer");
// Print the Type details
Console.WriteLine("Full Name = {0}",T.FullName);
Console.WriteLine("Just the Class Name = {0}",T.Name);
Console.WriteLine("Just the Namespace = {0}", T.Namespace);
Console.WriteLine();
// Print the list of Methods
Console.WriteLine("Methods in Customer Class");
MethodInfo[] methods = T.GetMethods();
foreach (MethodInfo method in methods)
{
// Print the Return type and the name of the method
Console.WriteLine(method.ReturnType.Name + " " + method.Name);
}
Console.WriteLine();
// Print the Properties
Console.WriteLine("Properties in Customer Class");
PropertyInfo[] properties = T.GetProperties();
foreach (PropertyInfo property in properties)
{
// Print the property type and the name of the property
Console.WriteLine(property.PropertyType.Name + " " + property.Name);
}
Console.WriteLine();
// Print the Constructors
Console.WriteLine("Constructors in Customer Class");
ConstructorInfo[] constructors = T.GetConstructors();
foreach (ConstructorInfo constructor in constructors)
{
Console.WriteLine(constructor.ToString());
}
}
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public Customer(int ID, string Name)
{
this.Id = ID;
this.Name = Name;
}
public Customer()
{
this.Id = -1;
this.Name = string.Empty;
}
public void PrintID()
{
Console.WriteLine("ID = {0}", this.Id);
}
public void PrintName()
{
Console.WriteLine("Name = {0}", this.Name);
}
}
}
In this example to get the type of customer class we have used GetType() static method defined on the Type class. We pass in the fully qualified name of the type including the namespace as a parameter to the GetType() method.
Type T = Type.GetType("Pragim.Customer");
To get the type information we have the following 2 ways as well.
Use typeof keyowrd
Type T = typeof(Customer);
Use GetType() on the instance of the customer class.
Customer C1 = new Customer();
Type T = C1.GetType();
To get the methods information, we use Type.GetMethods(), which returns MethodInfo[] array and along the same lines we use Type.GetProperties() to get properties information, but Type.GetProperties() returns PropertyInfo[] array.
Part
54 - C# Tutorial - Reflection Example
This session is based
on the previous session - Part 53 Reflection in C#. Please watch part 53,
before continuing with this session.
In this session we will develop a simple winforms application. Please design the form as shown in the image below.
In this session we will develop a simple winforms application. Please design the form as shown in the image below.
Part
54 - C# Tutorial - Reflection Example
In the properties window
Set the Name of the text box to txtTypeName
Set the Name of the button to btnDiscoverTypeInformation
Set the Name of the list boxes, to lstMethods, lstProperties, and lstConstructors
Now double click the button control to generate the event handler.
Copy and paste the following code in the button click event handler (btnDiscoverTypeInformation_Click).
string TypeName = txtTypeName.Text;
Type T = Type.GetType(TypeName);
lstMethods.Items.Clear();
lstProperties.Items.Clear();
lstConstructors.Items.Clear();
MethodInfo[] methods = T.GetMethods();
foreach (MethodInfo method in methods)
{
lstMethods.Items.Add(method.ReturnType.Name + " " + method.Name);
}
PropertyInfo[] properties = T.GetProperties();
foreach (PropertyInfo property in properties)
{
lstProperties.Items.Add(property.PropertyType.Name + " " + property.Name);
}
ConstructorInfo[] constructors = T.GetConstructors();
foreach (ConstructorInfo constructor in constructors)
{
lstConstructors.Items.Add(constructor.ToString());
}
Run the application and enter the type name for which you want to find type information. For example, if you enter System.Console, you should see the list of methods, properties and constructors.
Part 55 - C# Tutorial
- Late binding using reflection
In this session we will
understand
1. Early binding and late binding
2. The difference between the two approaches
Early Binding Example:
using System;
namespace Pragim
{
public class MainClass
{
private static void Main()
{
Customer C1 = new Customer();
string fullName = C1.GetFullName("Pragim", "Tech");
Console.WriteLine("Full Name = {0}", fullName);
}
}
public class Customer
{
public string GetFullName(string FirstName, string LastName)
{
return FirstName + " " + LastName;
}
}
}
1. Early binding and late binding
2. The difference between the two approaches
Early Binding Example:
using System;
namespace Pragim
{
public class MainClass
{
private static void Main()
{
Customer C1 = new Customer();
string fullName = C1.GetFullName("Pragim", "Tech");
Console.WriteLine("Full Name = {0}", fullName);
}
}
public class Customer
{
public string GetFullName(string FirstName, string LastName)
{
return FirstName + " " + LastName;
}
}
}
Part 55 - C# Tutorial - Late binding using
reflection
In this example, we have the knowledge of Customer class at compile time. So, we are able to create the instance of the Customer class using the new operator. We are also able to invoke the GetFullName() method using C1. Intellisense detects the presence of this method and the number and type of parameters that need to be passed in. If you make any mistake in the name of the method, or the number and type of parameters, those mistakes will be immediately raised as compiler errors.
Late Binding Example:
using System;
using System.Reflection;
namespace Pragim
{
public class MainClass
{
private static void Main()
{
// Load the current executing assembly as the Customer class is present in it.
Assembly executingAssembly = Assembly.GetExecutingAssembly();
// Load the Customer class for which we want to create an instance dynamically
Type customerType = executingAssembly.GetType("Pragim.Customer");
// Create the instance of the customer type using Activator class
object customerInstance = Activator.CreateInstance(customerType);
// Get the method information using the customerType and GetMethod()
MethodInfo getFullName = customerType.GetMethod("GetFullNames");
// Create the parameter array and populate first and last names
string[] methodParameters = new string[2];
methodParameters[0] = "Pragim"; //FirstName
methodParameters[1] = "Tech"; //LastName
// Invoke the method passing in customerInstance and parameters array
string fullName = (string)getFullName.Invoke(customerInstance, methodParameters);
Console.WriteLine("Full Name = {0}", fullName);
}
}
public class Customer
{
public string GetFullName(string FirstName, string LastName)
{
return FirstName + " " + LastName;
}
}
}
Let's assume we don't have the knowledge of Customer class at compile time, and it will be provided only at run time. In this case we need to bind to the Customer class at runtime.
1. Load the assembly which contains the Customer class. In our case, the Customer class is present in the same assembly as the MainClass. So, we use Assembly.GetExecutingAssembly() to load the current executing assembly. On the Assembly class, there are several static methods which can be used to load an assembly at runtime dynamically.
2. Next, we load the Customer class for which we want to create an instance dynamically using executingAssembly.GetType("Pragim.Customer"). Make sure you pass in the fully qualified name to the GetType() method, including the namespace. Otherwise you risk getting a NullReferenceException at runtime.
3. Create the instance of the Customer class using Activator.CreateInstance(customerType).
4. Once we have the Customer instance, now get the method information which we want to invoke dynamically. we use customerType.GetMethod("GetFullName").
5. The GetFullName() method expects 2 string parameters. So, we need to create a string array, and populate it with the first and last name parameters.
6. Finally, invoke the method passing in customerInstance and parameters array.
If you mis-spell the method name or if you pass in the wrong number or type of parameters, you wouldn't get a compiler error, but the application crashes at runtime.
Difference between early and late binding:
1. Early binding can flag errors at compile time. With late binding there is a risk of run time exceptions.
2. Early binding is much better for performance and should always be preferred over late binding. Use late binding only when working with onjects that are not available at compile time.
Part 56 - C# Tutorial - Generics in C#
Generics are introduced in C# 2.0. Generics allow us to design
classes and methods decoupled from the data types. Generic classes are
extensively used by collection classes available in System.Collections.Generic
namespace. Click here to watch
the video on generic collection classes.
In this example, AreEqual(int value1, int value2) only works with int data type. If, we pass any other data type, we get a compiler error. So, AreEqual() method in Calculator class is tightly coupled with the int data type, and prevents it from being used with any other data type.
In this example, AreEqual(int value1, int value2) only works with int data type. If, we pass any other data type, we get a compiler error. So, AreEqual() method in Calculator class is tightly coupled with the int data type, and prevents it from being used with any other data type.
Part
56 - C# Tutorial - Generics in C#
using System;
namespace Pragim
{
public class MainClass
{
private static void Main()
{
bool Equal = Calculator.AreEqual(1, 2);
if (Equal)
{
Console.WriteLine("Equal");
}
else
{
Console.WriteLine("Not Equal");
}
}
}
public class Calculator
{
public static bool AreEqual(int value1, int value2)
{
return value1 == value2;
}
}
}
It's a compile time error to invoke AreEqual() method with string parameters.
bool Equal = Calculator.AreEqual("A", "B");
One way of making AreEqual() method reusable, is to use object type parameters. Since, every type in .NET directly or indirectly inherit from System.Object type, AreEqual() method works with any data type, but the problem is performance degradation due to boxing and unboxing happening.
Also, AreEuqal() method is no longer type safe. It is now possible to pass integer for the first parameter, and a string for the second parameter. It doesn't really make sense to compare strings with integers.
using System;
namespace Pragim
{
public class MainClass
{
private static void Main()
{
bool Equal = Calculator.AreEqual("A", "B");
if (Equal)
{
Console.WriteLine("Equal");
}
else
{
Console.WriteLine("Not Equal");
}
}
}
public class Calculator
{
public static bool AreEqual(object value1, object value2)
{
return value1 == value2;
}
}
}
So, the probem with using System.Object type is that
1. AreEqual() method is not type safe
2. Performance degradation due to boxing and unboxing.
Both of these issues can be solved with generics and still make AreEqual() method work with different data types. The re written example using generics is shown below.
using System;
namespace Pragim
{
public class MainClass
{
private static void Main()
{
bool Equal = Calculator.AreEqual<int>(2, 1);
if (Equal)
{
Console.WriteLine("Equal");
}
else
{
Console.WriteLine("Not Equal");
}
}
}
public class Calculator
{
public static bool AreEqual<T>(T value1, T value2)
{
return value1.Equals(value2);
}
}
}
To make AreEqual() method generic, we specify a type parameter using angular brackets as shown below.
public static bool AreEqual<T>(T value1, T value2)
At the point, When the client code wants to invoke this method, they need to specify the type, they want the method to operate on. If the user wants the AreEqual() method to work with integers, they can invoke the method specifying int as the datatype using angular brackets as shown below.
bool Equal = Calculator.AreEqual<int>(2, 1);
To operate with string data type
bool Equal = Calculator.AreEqual<string>("A", "B");
In this example, we made the method generic. Along the same lines, it is also possible to make classes, interfaces and delegates generic.
In this session, let's
understand the difference between "==" operator and Equals()
method. In C#, every type directly or indirectly inherits
from System.Object. So, the Equals() virtual method, that has a
default implementation is available in every type via inheritance. In this
example, variables i and j are integers. So, == and Equals() method
returns true, since i and j, both variables have a value of 10.
using System;
namespace Pragim
{
public class MainClass
{
private static void Main()
{
int i = 10;
int j = 10;
Console.WriteLine(i == j);
Console.WriteLine(i.Equals(j));
}
}
}
using System;
namespace Pragim
{
public class MainClass
{
private static void Main()
{
int i = 10;
int j = 10;
Console.WriteLine(i == j);
Console.WriteLine(i.Equals(j));
}
}
}
Part 58 - C# Tutorial - Why should you
override Equals() method
Along the same lines, the sample program below, compares 2 enums and both, the == operator and Equals() method returns true, since bothe direction1 and direction2 enums has the same underlying integer value of 1.
using System;
namespace Pragim
{
public class MainClass
{
private static void Main()
{
Direction direction1 = Direction.East;
Direction direction2 = Direction.East;
Console.WriteLine(direction1 == direction2);
Console.WriteLine(direction1.Equals(direction2));
}
}
public enum Direction
{
East = 1,
West = 2,
North = 3,
South = 4
}
}
However, if the type is a reference type, then by default "==" operator checks for reference equality and .Equals() method checks for value equality. Let's understand what we mean by reference and value equality.
In the example below, C1 and C2 are 2 different object reference variables, but they point to the same object. Keep in mind, object reference variables are different from objects. Object reference variables, stay on the stack and are pointers to actual objects on the heap. Since, C1 and C2 both refer to the same object, the reference equality and value equality is true. Value equality means that two objects contain the same values. In this example, the actual object is only one, so obviously the values are also equal. If two objects have reference equality, then they also have value equality, but value equality does not guarantee reference equality.
using System;
namespace Pragim
{
public class MainClass
{
private static void Main()
{
Customer C1 = new Customer();
C1.FirstName = "Simon";
C1.LastName = "Tan";
Customer C2 = C1;
Console.WriteLine(C1 == C2);
Console.WriteLine(C1.Equals(C2));
}
}
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
For the example below, == operator returns False. This makes sense because C1 and C2 are referring to different objects. However, .Equals() method returns flase, inspite of the values across C1 and C2 being the same. Hence, it makes sense to override, the Equals() method to return true when the values across the objects are same.
using System;
namespace Pragim
{
public class MainClass
{
private static void Main()
{
Customer C1 = new Customer();
C1.FirstName = "Simon";
C1.LastName = "Tan";
Customer C2 = new Customer();
C2.FirstName = "Simon";
C2.LastName = "Tan";
Console.WriteLine(C1 == C2);
Console.WriteLine(C1.Equals(C2));
}
}
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
The example below overrides, Equals() method. When overriding Equals() method, make sure the passed in object is not null and can be casted to the type we are comparing. When overriding Equals(), you also need to override GetHashCode(), otherwise you get a compiler warning.
using System;
namespace Pragim
{
public class MainClass
{
private static void Main()
{
Customer C1 = new Customer();
C1.FirstName = "Simon";
C1.LastName = "Tan";
Customer C2 = new Customer();
C2.FirstName = "Simon";
C2.LastName = "Tan";
Console.WriteLine(C1 == C2);
Console.WriteLine(C1.Equals(C2));
}
}
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public override bool Equals(object obj)
{
// If the passed in object is null
if (obj == null)
{
return false;
}
if (!(obj is Customer))
{
return false;
}
return (this.FirstName == ((Customer)obj).FirstName)
&& (this.LastName == ((Customer)obj).LastName);
}
public override int GetHashCode()
{
return FirstName.GetHashCode() ^ LastName.GetHashCode();
}
}
}
Part 60 - C# Tutorial
- difference between System.String and System.Text.StringBuilder
Strings of type StringBuilder are
mutable where as strings of type System.String are immutable. As
StringBuilder objects are mutable, they offer better performance than string
objects of type System.String, when heavy string manipulation is involved.
Part 60 - C# Tutorial - difference between
System.String and System.Text.StringBuilder
Let's understand the meaning of mutable and immutable strings with an example.
using System;
public class MainClass
{
public static void Main()
{
string userString = "C#";
userString += " Video";
userString += " Tutorial";
userString += " for";
userString += " beginners";
Console.WriteLine(userString);
}
}
In this example, userString variable is changed 5 times.
1. C#
2. C# => C# Video
3. C# Video => C# Video Tutorial
4. C# Video Tutorial => C# Video Tutorial for
5. C# Video Tutorial for => C# Video Tutorial for beginners
Since, userString variable is of type System.String, and when we change this string 5 times, we end up with 5 string objects on the heap as shown in the diagram below. Immutable means, once a string object is created it cannot be changed, without creating another new string object. So in our example, When we initialize userString variable to "C#" we get one immutable string object on the heap. When we concatenate " Video" word to userString variable, the first created "C#" string object is orphaned(userString variable no longer points to this object). Now another new string object with words "C# Video" will be created to which the userString variable points to. So this process continues until, userString reference variable, points to the last string object (C# Video Tutorial for beginners), leaving the other 4 string onbjects on the heap(orphaned), until they are garbage collected, increasing the pressure on memory.
But on the other hand, StringBuilder string objects are mutable, meaning they can be changed inplace, without the need of creating another new StringBuilder object. The above example is rewritten using StringBuilder object.
using System;
using System.Text;
namespace Pragim
{
public class MainClass
{
public static void Main()
{
StringBuilder userStringBuilder =
new StringBuilder("C#");
userStringBuilder.Append(" Video");
userStringBuilder.Append(" Tutorial");
userStringBuilder.Append(" for");
userStringBuilder.Append(" beginners");
Console.WriteLine(userStringBuilder.ToString());
}
}
}
With StringBuilder, no matter how many times you manipulate a string, you will ever have only one instance.
So in brief, here are the differences between String and StringBuilderobjects.
1. Objects of type StringBuilder are mutable where as objects of type System.String are immutable.
2. As StringBuilder objects are mutable, they offer better performance than string objects of type System.String.
3. StringBuilder class is present in System.Text namespace where String class is present in System namespace.
Just imagine, the number of orphaned string objects that get created on the heap when you have a program as shown below.
using System;
namespace Pragim
{
public class MainClass
{
public static void Main()
{
string strNumbers = string.Empty;
for (int i = 0; i < 1000; i++)
{
strNumbers += i.ToString() + " ";
}
Console.WriteLine(strNumbers);
}
}
}
In this video we will
discuss about
1. What are partial classes?
2. What are the advantages of using partial classes?
3. Where are partial classes used?
Partial classes allow us to split a class into 2 or more files. All these parts are then combined into a single class, when the application is compiled. The partial keyword can also be used to split a struct or an interface over two or more files.
Let's understand partial classes with an example.Create an asp.net web application project. Add a class file, with name Customer.cs to the project. Copy and paste the following code in the customer.cs file. This is a very simple customer class, with 2 private fields, 2 public properties and a public method.
public class Customer
{
private string _firstName;
private string _lastName;
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
public string GetFullName()
{
return _firstName + ", " + _lastName;
}
}
Now, let us split this class into 2 files. One file is going to contain, the private fields and public properties, and the other file is going to contain the public method. Right click on the web application project, and add a class file, with name PartialCustomerOne.cs. Notice, that the PartialCustomer class is marked with the partial keyword and it contains, only, the 2 private fields and the public properties.
public partial class PartialCustomer
{
private string _firstName;
private string _lastName;
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
}
Now, add another class file with name, PartialCustomerTwo.cs. Notice that, the PartialCustomer class, in this file is also marked as a partial class, and contains only the public method - GetFullName(). We are able to access the private fields, _firstName and _lastName, that are defined in PartialCustomerOne.cs file.
public partial class PartialCustomer
{
public string GetFullName()
{
return _firstName + ", " + _lastName;
}
}
Copy and paste the following code in the Page_Load() event of the webform1. Though, the PartialCustomer class is split across 2 files(PartialCustomerOne.cs and PartialCustomerTwo.cs), we are able to use it the same way as the Customer class.
Customer c1 = new Customer();
c1.FirstName = "Pragim";
c1.LastName = "Technologies";
string FullName1 = c1.GetFullName();
Response.Write("Full Name = " + FullName1 + "<br/>");
PartialCustomer c2 = new PartialCustomer();
c2.FirstName = "Pragim";
c2.LastName = "Tech";
string FullName2 = c2.GetFullName();
Response.Write("Full Name = " + FullName2 + "<br/>");
Advantages of partial classes
1. The main advantage is that, visual studio uses partial classes to separate, automatically generated system code from the developer's code. For example, when you add a webform, two .CS files are generated
a) WebForm1.aspx.cs - Contains the developer code
b) WebForm1.aspx.designer.cs - Contains the system generated code. For example, declarations for the controls that you drag and drop on the webform.
2. When working on large projects, spreading a class over separate files allows multiple programmers to work on it simultaneously. Though, microsoft claims this as an advantage, I haven't really seen anywhere, people using partial classes, just to work on them simultaneously.
1. What are partial classes?
2. What are the advantages of using partial classes?
3. Where are partial classes used?
Partial classes allow us to split a class into 2 or more files. All these parts are then combined into a single class, when the application is compiled. The partial keyword can also be used to split a struct or an interface over two or more files.
Let's understand partial classes with an example.Create an asp.net web application project. Add a class file, with name Customer.cs to the project. Copy and paste the following code in the customer.cs file. This is a very simple customer class, with 2 private fields, 2 public properties and a public method.
public class Customer
{
private string _firstName;
private string _lastName;
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
public string GetFullName()
{
return _firstName + ", " + _lastName;
}
}
Now, let us split this class into 2 files. One file is going to contain, the private fields and public properties, and the other file is going to contain the public method. Right click on the web application project, and add a class file, with name PartialCustomerOne.cs. Notice, that the PartialCustomer class is marked with the partial keyword and it contains, only, the 2 private fields and the public properties.
public partial class PartialCustomer
{
private string _firstName;
private string _lastName;
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
}
Now, add another class file with name, PartialCustomerTwo.cs. Notice that, the PartialCustomer class, in this file is also marked as a partial class, and contains only the public method - GetFullName(). We are able to access the private fields, _firstName and _lastName, that are defined in PartialCustomerOne.cs file.
public partial class PartialCustomer
{
public string GetFullName()
{
return _firstName + ", " + _lastName;
}
}
Copy and paste the following code in the Page_Load() event of the webform1. Though, the PartialCustomer class is split across 2 files(PartialCustomerOne.cs and PartialCustomerTwo.cs), we are able to use it the same way as the Customer class.
Customer c1 = new Customer();
c1.FirstName = "Pragim";
c1.LastName = "Technologies";
string FullName1 = c1.GetFullName();
Response.Write("Full Name = " + FullName1 + "<br/>");
PartialCustomer c2 = new PartialCustomer();
c2.FirstName = "Pragim";
c2.LastName = "Tech";
string FullName2 = c2.GetFullName();
Response.Write("Full Name = " + FullName2 + "<br/>");
Advantages of partial classes
1. The main advantage is that, visual studio uses partial classes to separate, automatically generated system code from the developer's code. For example, when you add a webform, two .CS files are generated
a) WebForm1.aspx.cs - Contains the developer code
b) WebForm1.aspx.designer.cs - Contains the system generated code. For example, declarations for the controls that you drag and drop on the webform.
2. When working on large projects, spreading a class over separate files allows multiple programmers to work on it simultaneously. Though, microsoft claims this as an advantage, I haven't really seen anywhere, people using partial classes, just to work on them simultaneously.
Part 62 - Creating partial classes in c#
Suggested
Videos
Part 61 - Partial classes in c#
1. All the parts spread across different files, must use the partial keyword. Otherwise a compiler error is raised.
Missing partial modifier. Another partial declaration of this type exists
2. All the parts spread across different files, must have the same access modifiers. Otherwise a compiler error is raised.
Partial declarations have conflicting accessibility modifiers
3. If any of the parts are declared abstract, then the entire type is considered abstract.
4. If any of the parts are declared sealed, then the entire type is considered sealed.
5. If any of the parts inherit a class, then the entire type inherits that class.
6. C# does not support multiple class inheritance. Different parts of the partial class, must not specify different base classes. The following code will raise a compiler error stating - Partial declarations must not specify different base classes.
public partial class SamplePartialClass : Employee
{
}
public partial class SamplePartialClass : Customer
{
}
public class Employee
{
}
public class Customer
{
}
7. Different parts of the partial class can specify different base interfaces, and the final type implements all of the interfaces listed by all of the partial declarations. In the example below, SamplePartialClass needs to provide implementation for both IEmployee, and ICustomer interface methods.
public partial class SamplePartialClass : IEmployee
{
public void EmployeeMethod()
{
//Method Implementation
}
}
public partial class SamplePartialClass : ICustomer
{
public void CustomerMethod()
{
//Method Implementation
}
}
public interface IEmployee
{
void EmployeeMethod();
}
public interface ICustomer
{
void CustomerMethod();
}
8. Any members that are declared in a partial definition are available to all of the other parts of the partial class.
Part 61 - Partial classes in c#
1. All the parts spread across different files, must use the partial keyword. Otherwise a compiler error is raised.
Missing partial modifier. Another partial declaration of this type exists
2. All the parts spread across different files, must have the same access modifiers. Otherwise a compiler error is raised.
Partial declarations have conflicting accessibility modifiers
3. If any of the parts are declared abstract, then the entire type is considered abstract.
4. If any of the parts are declared sealed, then the entire type is considered sealed.
5. If any of the parts inherit a class, then the entire type inherits that class.
6. C# does not support multiple class inheritance. Different parts of the partial class, must not specify different base classes. The following code will raise a compiler error stating - Partial declarations must not specify different base classes.
public partial class SamplePartialClass : Employee
{
}
public partial class SamplePartialClass : Customer
{
}
public class Employee
{
}
public class Customer
{
}
7. Different parts of the partial class can specify different base interfaces, and the final type implements all of the interfaces listed by all of the partial declarations. In the example below, SamplePartialClass needs to provide implementation for both IEmployee, and ICustomer interface methods.
public partial class SamplePartialClass : IEmployee
{
public void EmployeeMethod()
{
//Method Implementation
}
}
public partial class SamplePartialClass : ICustomer
{
public void CustomerMethod()
{
//Method Implementation
}
}
public interface IEmployee
{
void EmployeeMethod();
}
public interface ICustomer
{
void CustomerMethod();
}
8. Any members that are declared in a partial definition are available to all of the other parts of the partial class.
Partial
methods in C# - Part 63
Suggested
Videos
Part 61 - Partial classes in C#
Part 62 - Creating partial classes in c#
A partial class or a struct can contain partial methods. A partial method is created using the partial keyword. Let us understand partial methods with an example. Create a console application. Add a class file, with name PartialClassFileOne.cs, to the project. copy and paste the following code.
Notice, that, the SampleMethod() definition has the partial keyword, and does not have a body(implementation) only the signature. The implementation for a partial method is optional. If we don't provide the implementation, the compiler removes the signature and all calls to the method.
The implementation can be provided in the same physical file, or in another physical file, that contains the partial class. In this example, the partial SampleMethod() is invoked in the PublicMethod().
partial class SampleClass
{
// Declaration of the partial method.
partial void SampleMethod();
// A public method calling the partial method
public void PublicMethod()
{
Console.WriteLine("Public Method Invoked");
SampleMethod();
}
}
Copy and paste the following code in the Main() method of the console application. When we run the application now, notice that, we don't get a compiler error, in spite of not having an implementation for the partial SampleMethod(). Since, the implementation for the partial method is missing, the compiler will remove the signature and all calls to the method.
SampleClass SC = new SampleClass();
SC.PublicMethod();
Now, add a class file, with name PartialClassFileTwo.cs. Copy and paste the following code. The implementation for the partial method is provided here.
partial class SampleClass
{
// Partial method implemented
partial void SampleMethod()
{
Console.WriteLine("Partial SampleMethod Invoked");
}
}
Now, run the console application and notice the output. The partial method and the public method messages are printed on the console.
A partial method declaration consists of two parts.
1. The definition (only the method signature ending with a semi-colon, without method body)
2. The implementation.
These may be in separate parts of a partial class, or in the same part.
Partial methods are private by default, and it is a compile time error to include any access modifiers, including private. The following code will raise an error stating - A partial method cannot have access modifiers or the virtual, abstract, override, new, sealed, or extern modifiers.
partial class SampleClass
{
private partial void SampleMethod();
}
It is a compile time error, to include declaration and implementation at the same time for a partial method. Code below produces a compile time error - No defining declaration found for implementing declaration of partial method 'PartialMethodsDemo.SampleClass.SampleMethod()'
partial class SampleClass
{
partial void SampleMethod()
{
Console.WriteLine("SampleMethod Implemented");
}
}
A partial method return type must be void. Including any other return type is a compile time error - Partial methods must have a void return type
partial class SampleClass
{
partial int SampleMethod();
}
A partial method must be declared within a partial class or partial struct. A non partial class or struct cannot include partial methods.
Signature of the partial method declaration, must match with the signature of the implementation.
A partial method can be implemented only once. Trying to implement a partial method more than once, raises a compile time error - A partial method may not have multiple implementing declarations.
Part 61 - Partial classes in C#
Part 62 - Creating partial classes in c#
A partial class or a struct can contain partial methods. A partial method is created using the partial keyword. Let us understand partial methods with an example. Create a console application. Add a class file, with name PartialClassFileOne.cs, to the project. copy and paste the following code.
Notice, that, the SampleMethod() definition has the partial keyword, and does not have a body(implementation) only the signature. The implementation for a partial method is optional. If we don't provide the implementation, the compiler removes the signature and all calls to the method.
The implementation can be provided in the same physical file, or in another physical file, that contains the partial class. In this example, the partial SampleMethod() is invoked in the PublicMethod().
partial class SampleClass
{
// Declaration of the partial method.
partial void SampleMethod();
// A public method calling the partial method
public void PublicMethod()
{
Console.WriteLine("Public Method Invoked");
SampleMethod();
}
}
Copy and paste the following code in the Main() method of the console application. When we run the application now, notice that, we don't get a compiler error, in spite of not having an implementation for the partial SampleMethod(). Since, the implementation for the partial method is missing, the compiler will remove the signature and all calls to the method.
SampleClass SC = new SampleClass();
SC.PublicMethod();
Now, add a class file, with name PartialClassFileTwo.cs. Copy and paste the following code. The implementation for the partial method is provided here.
partial class SampleClass
{
// Partial method implemented
partial void SampleMethod()
{
Console.WriteLine("Partial SampleMethod Invoked");
}
}
Now, run the console application and notice the output. The partial method and the public method messages are printed on the console.
A partial method declaration consists of two parts.
1. The definition (only the method signature ending with a semi-colon, without method body)
2. The implementation.
These may be in separate parts of a partial class, or in the same part.
Partial methods are private by default, and it is a compile time error to include any access modifiers, including private. The following code will raise an error stating - A partial method cannot have access modifiers or the virtual, abstract, override, new, sealed, or extern modifiers.
partial class SampleClass
{
private partial void SampleMethod();
}
It is a compile time error, to include declaration and implementation at the same time for a partial method. Code below produces a compile time error - No defining declaration found for implementing declaration of partial method 'PartialMethodsDemo.SampleClass.SampleMethod()'
partial class SampleClass
{
partial void SampleMethod()
{
Console.WriteLine("SampleMethod Implemented");
}
}
A partial method return type must be void. Including any other return type is a compile time error - Partial methods must have a void return type
partial class SampleClass
{
partial int SampleMethod();
}
A partial method must be declared within a partial class or partial struct. A non partial class or struct cannot include partial methods.
Signature of the partial method declaration, must match with the signature of the implementation.
A partial method can be implemented only once. Trying to implement a partial method more than once, raises a compile time error - A partial method may not have multiple implementing declarations.
Part 64 - C# Tutorial - How and where are
indexers used in .net
Suggested
Videos
Part 61 - Partial classes
Part 62 - Creating partial classes
Part 63 - Partial methods
In this video we will discuss about
1. Where are indexers used in .NET
2. What are indexers in c#
Where are indexers used in .NET
To store or retrieve data from session state or application state variables, we use indexers.
// Using the string indexer to store session data
Session["Session1"] = "Session 1 Data";
// Using the string indexer to store session data
Session["Session2"] = "Session 2 Data";
// Using the integral indexer to retrieve data
Response.Write("Session 1 Data = " + Session[0].ToString());
Response.Write("<br/>");
// Using the string indexer to retrieve data
Response.Write("Session 2 Data = " + Session["Session2"].ToString());
If you view the metadata of HttpSessionState class, you can see that there is an integral and string indexer defined. We use "this" keyword to create indexers in c#. We will discuss about creating indexers in our next video session.
Another example of indexers usage in .NET. To retrieve data from a specific column when looping thru "SqlDataReader" object, we can use either the integral indexer or string indexer.
string CS = ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;
using (SqlConnection con = new SqlConnection(CS))
{
SqlCommand cmd = new SqlCommand("Select * from tblEmployee", con);
con.Open();
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
// Using integral indexer to retrieve Id column value
Response.Write("Id = " + rdr[0].ToString() + " ");
// Using string indexer to retrieve Id column value
Response.Write("Name = " + rdr["Name"].ToString());
Response.Write("<br/>");
}
}
Right click on SqlDataReader class and select "Go To Definition", to view it's metadata. Notice that, there is an integral and string indexer defined.
What are indexers in c#?
From the above examples, it should be clear that, Indexers allow instances of a class to be indexed just like arrays.
In our next video, we will discuss about creating indexers.
Part 61 - Partial classes
Part 62 - Creating partial classes
Part 63 - Partial methods
In this video we will discuss about
1. Where are indexers used in .NET
2. What are indexers in c#
Where are indexers used in .NET
To store or retrieve data from session state or application state variables, we use indexers.
// Using the string indexer to store session data
Session["Session1"] = "Session 1 Data";
// Using the string indexer to store session data
Session["Session2"] = "Session 2 Data";
// Using the integral indexer to retrieve data
Response.Write("Session 1 Data = " + Session[0].ToString());
Response.Write("<br/>");
// Using the string indexer to retrieve data
Response.Write("Session 2 Data = " + Session["Session2"].ToString());
If you view the metadata of HttpSessionState class, you can see that there is an integral and string indexer defined. We use "this" keyword to create indexers in c#. We will discuss about creating indexers in our next video session.
Another example of indexers usage in .NET. To retrieve data from a specific column when looping thru "SqlDataReader" object, we can use either the integral indexer or string indexer.
string CS = ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;
using (SqlConnection con = new SqlConnection(CS))
{
SqlCommand cmd = new SqlCommand("Select * from tblEmployee", con);
con.Open();
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
// Using integral indexer to retrieve Id column value
Response.Write("Id = " + rdr[0].ToString() + " ");
// Using string indexer to retrieve Id column value
Response.Write("Name = " + rdr["Name"].ToString());
Response.Write("<br/>");
}
}
Right click on SqlDataReader class and select "Go To Definition", to view it's metadata. Notice that, there is an integral and string indexer defined.
What are indexers in c#?
From the above examples, it should be clear that, Indexers allow instances of a class to be indexed just like arrays.
In our next video, we will discuss about creating indexers.
Part 65 - C# Tutorial - Indexers in c#
Suggested
Videos
Part 62 - Creating partial classes
Part 63 - Partial methods
Part 64 - How and where are indexers used in .net
Please watch Part 64, before proceeding with this video.
In this video we will discuss about creating indexers. Let us understand indexers with an example. Create an asp.net web application. Add a class file, with name = Company.cs. Copy and paste the following code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Demo
{
public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
}
public class Company
{
private List<Employee> listEmployees;
public Company()
{
listEmployees = new List<Employee>();
listEmployees.Add(new Employee
{ EmployeeId = 1, Name = "Mike", Gender = "Male" });
listEmployees.Add(new Employee
{ EmployeeId = 2, Name = "Pam", Gender = "Female" });
listEmployees.Add(new Employee
{ EmployeeId = 3, Name = "John", Gender = "Male" });
listEmployees.Add(new Employee
{ EmployeeId = 4, Name = "Maxine", Gender = "Female" });
listEmployees.Add(new Employee
{ EmployeeId = 5, Name = "Emiliy", Gender = "Female" });
listEmployees.Add(new Employee
{ EmployeeId = 6, Name = "Scott", Gender = "Male" });
listEmployees.Add(new Employee
{ EmployeeId = 7, Name = "Todd", Gender = "Male" });
listEmployees.Add(new Employee
{ EmployeeId = 8, Name = "Ben", Gender = "Male" });
}
// Use "this" keyword to create an indexer
// This indexer takes employeeId as parameter
// and returns employee name
public string this[int employeeId]
{
// Just like properties indexers have get and set accessors
get
{
return listEmployees.
FirstOrDefault(x => x.EmployeeId == employeeId).Name;
}
set
{
listEmployees.
FirstOrDefault(x => x.EmployeeId == employeeId).Name = value;
}
}
}
}
Points to remember:
1. In the Company class constructor, we are initializing variable "listEmployees" and adding employees to the list.
2. We then created an indexer using "this" keyword. This indexer takes employeeId as parameter and returns employee name.
public string this[int employeeId]
3. Just like properties indexers have get and set accessors.
4. Indexers can also be overloaded. We will discuss about indexer overloading in our next video.
Now let's discuss about, using the indexer, that we just created. Copy and paste the following code in WebForm1.aspx.cs
Company company = new Company();
Response.Write("Name of Employee with Id = 2: " + company[2]);
Response.Write("<br/>");
Response.Write("Name of Employee with Id = 5: " + company[5]);
Response.Write("<br/>");
Response.Write("Name of Employee with Id = 8: " + company[8]);
Response.Write("<br/>");
Response.Write("<br/>");
Response.Write("Changing names of employees with Id = 2,5,8");
Response.Write("<br/>");
company[2] = "Employee 2 Name Changed";
company[5] = "Employee 5 Name Changed";
company[8] = "Employee 8 Name Changed";
Response.Write("Name of Employee with Id = 2: " + company[2]);
Response.Write("<br/>");
Response.Write("Name of Employee with Id = 5: " + company[5]);
Response.Write("<br/>");
Response.Write("Name of Employee with Id = 8: " + company[8]);
Points to remember:
1. EmployeeId's 2,5 and 8 are passed into the company object, to retrieve the respective employee names. To retrieve the names of the employees, the "get" accessor of the indexer is used.
2. To change the names of employees, we are again using the integral indexer defined on Company class.
company[2] = "Employee 2 Name Changed";
Notice that, because of the "employeeId" indexer, I am able to use company object like an array.
Part 62 - Creating partial classes
Part 63 - Partial methods
Part 64 - How and where are indexers used in .net
Please watch Part 64, before proceeding with this video.
In this video we will discuss about creating indexers. Let us understand indexers with an example. Create an asp.net web application. Add a class file, with name = Company.cs. Copy and paste the following code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Demo
{
public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
}
public class Company
{
private List<Employee> listEmployees;
public Company()
{
listEmployees = new List<Employee>();
listEmployees.Add(new Employee
{ EmployeeId = 1, Name = "Mike", Gender = "Male" });
listEmployees.Add(new Employee
{ EmployeeId = 2, Name = "Pam", Gender = "Female" });
listEmployees.Add(new Employee
{ EmployeeId = 3, Name = "John", Gender = "Male" });
listEmployees.Add(new Employee
{ EmployeeId = 4, Name = "Maxine", Gender = "Female" });
listEmployees.Add(new Employee
{ EmployeeId = 5, Name = "Emiliy", Gender = "Female" });
listEmployees.Add(new Employee
{ EmployeeId = 6, Name = "Scott", Gender = "Male" });
listEmployees.Add(new Employee
{ EmployeeId = 7, Name = "Todd", Gender = "Male" });
listEmployees.Add(new Employee
{ EmployeeId = 8, Name = "Ben", Gender = "Male" });
}
// Use "this" keyword to create an indexer
// This indexer takes employeeId as parameter
// and returns employee name
public string this[int employeeId]
{
// Just like properties indexers have get and set accessors
get
{
return listEmployees.
FirstOrDefault(x => x.EmployeeId == employeeId).Name;
}
set
{
listEmployees.
FirstOrDefault(x => x.EmployeeId == employeeId).Name = value;
}
}
}
}
Points to remember:
1. In the Company class constructor, we are initializing variable "listEmployees" and adding employees to the list.
2. We then created an indexer using "this" keyword. This indexer takes employeeId as parameter and returns employee name.
public string this[int employeeId]
3. Just like properties indexers have get and set accessors.
4. Indexers can also be overloaded. We will discuss about indexer overloading in our next video.
Now let's discuss about, using the indexer, that we just created. Copy and paste the following code in WebForm1.aspx.cs
Company company = new Company();
Response.Write("Name of Employee with Id = 2: " + company[2]);
Response.Write("<br/>");
Response.Write("Name of Employee with Id = 5: " + company[5]);
Response.Write("<br/>");
Response.Write("Name of Employee with Id = 8: " + company[8]);
Response.Write("<br/>");
Response.Write("<br/>");
Response.Write("Changing names of employees with Id = 2,5,8");
Response.Write("<br/>");
company[2] = "Employee 2 Name Changed";
company[5] = "Employee 5 Name Changed";
company[8] = "Employee 8 Name Changed";
Response.Write("Name of Employee with Id = 2: " + company[2]);
Response.Write("<br/>");
Response.Write("Name of Employee with Id = 5: " + company[5]);
Response.Write("<br/>");
Response.Write("Name of Employee with Id = 8: " + company[8]);
Points to remember:
1. EmployeeId's 2,5 and 8 are passed into the company object, to retrieve the respective employee names. To retrieve the names of the employees, the "get" accessor of the indexer is used.
2. To change the names of employees, we are again using the integral indexer defined on Company class.
company[2] = "Employee 2 Name Changed";
Notice that, because of the "employeeId" indexer, I am able to use company object like an array.
Part 66 - C# Tutorial - Overloading
indexers in c#
Suggested
Videos
Part 63 - Partial methods
Part 64 - How and where are indexers used in .net
Part 65 - Indexers in c#
In this video we will discuss about Overloading indexers in c#. Please watch Part 64 and Part 65 before proceeding. We will be modifying the example, that we discussed in Part 65.
In Part 65, we discussed about creating an indexer based on integer parameter.
public string this[int employeeId]
{
get
{
return listEmployees.
FirstOrDefault(x => x.EmployeeId == employeeId).Name;
}
set
{
listEmployees.
FirstOrDefault(x => x.EmployeeId == employeeId).Name = value;
}
}
Now let us create another indexer based on a string parameter.
public string this[string gender]
{
get
{
// Returns the total count of employees whose gender matches
// with the gender that is passed in.
return listEmployees.Count(x => x.Gender == gender).ToString();
}
set
{
// Changes the gender of all employees whose gender matches
// with the gender that is passed in.
foreach (Employee employee in listEmployees)
{
if (employee.Gender == gender)
{
employee.Gender = value;
}
}
}
}
Please note that, indexers can be overloaded based on the number and type of parameters.
Here is the complete code of Company class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
namespace Demo
{
public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
}
public class Company
{
private List<Employee> listEmployees;
public Company()
{
listEmployees = new List<Employee>();
listEmployees.Add(new Employee
{ EmployeeId = 1, Name = "Mike", Gender = "Male" });
listEmployees.Add(new Employee
{ EmployeeId = 2, Name = "Pam", Gender = "Female" });
listEmployees.Add(new Employee
{ EmployeeId = 3, Name = "John", Gender = "Male" });
listEmployees.Add(new Employee
{ EmployeeId = 4, Name = "Maxine", Gender = "Female" });
listEmployees.Add(new Employee
{ EmployeeId = 5, Name = "Emiliy", Gender = "Female" });
listEmployees.Add(new Employee
{ EmployeeId = 6, Name = "Scott", Gender = "Male" });
listEmployees.Add(new Employee
{ EmployeeId = 7, Name = "Todd", Gender = "Male" });
listEmployees.Add(new Employee
{ EmployeeId = 8, Name = "Ben", Gender = "Male" });
}
public string this[int employeeId]
{
get
{
return listEmployees.
FirstOrDefault(x => x.EmployeeId == employeeId).Name;
}
set
{
listEmployees.
FirstOrDefault(x => x.EmployeeId == employeeId).Name = value;
}
}
public string this[string gender]
{
get
{
return listEmployees.Count(x => x.Gender == gender).ToString();
}
set
{
foreach (Employee employee in listEmployees)
{
if (employee.Gender == gender)
{
employee.Gender = value;
}
}
}
}
}
}
Notice that the Company class has 2 indexers. The first indexer has an integer (employeeId) parameter and the second indexer has got a string (gender) parameter.
To test the string indexer, that we have just created, copy and paste the following code in Page_Load() event of WebForm1.aspx.cs
Company company = new Company();
Response.Write("Before changing the Gender of all male employees to Female");
Response.Write("<br/>");
// Get accessor of string indexer is invoked to return the total
// count of male employees
Response.Write("Total Employees with Gender = Male:" + company["Male"]);
Response.Write("<br/>");
Response.Write("Total Employees with Gender = Female:" + company["Female"]);
Response.Write("<br/>");
Response.Write("<br/>");
// Set accessor of string indexer is invoked to change the gender
// all "Male" employees to "Female"
company["Male"] = "Female";
Response.Write("After changing the Gender of all male employees to Female");
Response.Write("<br/>");
Response.Write("Total Employees with Gender = Male:" + company["Male"]);
Response.Write("<br/>");
Response.Write("Total Employees with Gender = Female:" + company["Female"]);
Part 63 - Partial methods
Part 64 - How and where are indexers used in .net
Part 65 - Indexers in c#
In this video we will discuss about Overloading indexers in c#. Please watch Part 64 and Part 65 before proceeding. We will be modifying the example, that we discussed in Part 65.
In Part 65, we discussed about creating an indexer based on integer parameter.
public string this[int employeeId]
{
get
{
return listEmployees.
FirstOrDefault(x => x.EmployeeId == employeeId).Name;
}
set
{
listEmployees.
FirstOrDefault(x => x.EmployeeId == employeeId).Name = value;
}
}
Now let us create another indexer based on a string parameter.
public string this[string gender]
{
get
{
// Returns the total count of employees whose gender matches
// with the gender that is passed in.
return listEmployees.Count(x => x.Gender == gender).ToString();
}
set
{
// Changes the gender of all employees whose gender matches
// with the gender that is passed in.
foreach (Employee employee in listEmployees)
{
if (employee.Gender == gender)
{
employee.Gender = value;
}
}
}
}
Please note that, indexers can be overloaded based on the number and type of parameters.
Here is the complete code of Company class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
namespace Demo
{
public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
}
public class Company
{
private List<Employee> listEmployees;
public Company()
{
listEmployees = new List<Employee>();
listEmployees.Add(new Employee
{ EmployeeId = 1, Name = "Mike", Gender = "Male" });
listEmployees.Add(new Employee
{ EmployeeId = 2, Name = "Pam", Gender = "Female" });
listEmployees.Add(new Employee
{ EmployeeId = 3, Name = "John", Gender = "Male" });
listEmployees.Add(new Employee
{ EmployeeId = 4, Name = "Maxine", Gender = "Female" });
listEmployees.Add(new Employee
{ EmployeeId = 5, Name = "Emiliy", Gender = "Female" });
listEmployees.Add(new Employee
{ EmployeeId = 6, Name = "Scott", Gender = "Male" });
listEmployees.Add(new Employee
{ EmployeeId = 7, Name = "Todd", Gender = "Male" });
listEmployees.Add(new Employee
{ EmployeeId = 8, Name = "Ben", Gender = "Male" });
}
public string this[int employeeId]
{
get
{
return listEmployees.
FirstOrDefault(x => x.EmployeeId == employeeId).Name;
}
set
{
listEmployees.
FirstOrDefault(x => x.EmployeeId == employeeId).Name = value;
}
}
public string this[string gender]
{
get
{
return listEmployees.Count(x => x.Gender == gender).ToString();
}
set
{
foreach (Employee employee in listEmployees)
{
if (employee.Gender == gender)
{
employee.Gender = value;
}
}
}
}
}
}
Notice that the Company class has 2 indexers. The first indexer has an integer (employeeId) parameter and the second indexer has got a string (gender) parameter.
To test the string indexer, that we have just created, copy and paste the following code in Page_Load() event of WebForm1.aspx.cs
Company company = new Company();
Response.Write("Before changing the Gender of all male employees to Female");
Response.Write("<br/>");
// Get accessor of string indexer is invoked to return the total
// count of male employees
Response.Write("Total Employees with Gender = Male:" + company["Male"]);
Response.Write("<br/>");
Response.Write("Total Employees with Gender = Female:" + company["Female"]);
Response.Write("<br/>");
Response.Write("<br/>");
// Set accessor of string indexer is invoked to change the gender
// all "Male" employees to "Female"
company["Male"] = "Female";
Response.Write("After changing the Gender of all male employees to Female");
Response.Write("<br/>");
Response.Write("Total Employees with Gender = Male:" + company["Male"]);
Response.Write("<br/>");
Response.Write("Total Employees with Gender = Female:" + company["Female"]);
No comments:
Post a Comment