Saturday 15 January 2022

 

1) What is Access modifiers in C# ?

Access modifiers are  used to specify the scope of accessibility of a member of a class or type of the class itself. For example, a public class is accessible to everyone without any restrictions, while an internal class may be accessible to the containing assembly only.

Types : 

Private: limits the accessibility of a member to within the defined type, for example if a variable or a functions is being created in a ClassA and declared as private then another ClassB can't access that.

Public: has no limits, any members or types defined as public can be accessed within the class, assembly even outside the assembly. Most DLLs are known to be produced by public class and members written in a .cs file.

Internal: internal plays an important role when you want your class members to be accessible within the assembly. An assembly is the produced .dll or .exe from your .NET Language code (C#). Hence, if you have a C# project that has ClassA, ClassB and ClassC then any internal type and members will become accessible across the classes with in the assembly.

Protected:
 plays a role only when inheritance is used. In other words any protected type or member becomes accessible when a child is inherited by the parent. In other cases (when no inheritance) protected members and types are not visible.

Protected internal:
 is a combination of protected and internal both. A protected internal will be accessible within the assembly due to its internal flavor and also via inheritance due to its protected flavor.

Code :

1) Lets create a console c# application.
2) Create a new class file and add the code as below ,

class Class1
    {
        //Available only to the container Class 
        private string privateVariable;

        // Available in entire assembly across the classes 
        internal string internalVariable;

        //Available in the container class and the derived class   
        protected string protectedVariable;

        //Available to the container class, entire assembly and to outside    
        public string publicVariable;

        //Available to the derived class and entire assembly as well
        protected internal string protectedInternalVariable;

        private string PrivateFunction()
        {
            privateVariable = "";
            return privateVariable;
        }

        internal string InternalFunction()
        {
            privateVariable = "";
            internalVariable = "";
            return internalVariable;
        }

        protected string ProtectedFunction()
        {
            privateVariable = "";
            internalVariable = "";
            return protectedVariable;
        }

        public string PublicFunction()
        {
            privateVariable = "";
            internalVariable = "";
            return publicVariable;
        }

        protected internal string ProtectedInternalFunction()
        {
            privateVariable = "";
            internalVariable = "";
            return protectedInternalVariable;
        }
    }

3) Go to your program.cs file, In main method create object for your class and try to access the created fields with the object. 

You can see from below image only Internal, Protected Internal and public variable fields can be accessible.

Private is not shown due to its limited scope with in the class.

Protected members also not showing in below, because protected members can be accessed with derived class inheriting parent class.
 



Create a new Console Application project and create a class file, create a method in that class. Add reference of our above project to this project. Create a instance of Class1 of our previous project in this project method , You can see only public fields and methods can be accessible, if our above project class is private it cannot be accessed in this project. By default the access modifier of class is Internal  and private for class members.



Inherit from Class1 of that project to this project class as below image,



You can see in test1 method, we can able to see protected , protected Internal and public methods and fields with this keyword after inheriting from parent cclass in that project.

Internal will be accessed only with in assembly meaning with that  project dll or exe.

 

Value Type and Reference Type

In C#, these data types are categorized based on how they store their value in the memory. C# includes the following categories of data types:

  1. Value type
  2. Reference type
  3. Pointer type
All the value types derive from System.ValueType, which in-turn, derives from System.Object.

Value Type

A data type is a value type if it holds a data value within its own memory space. It means the variables of these data types directly contain values.

For example, consider integer variable int i = 100;

The system stores 100 in the memory space allocated for the variable i. The following image illustrates how 100 is stored at some hypothetical location in the memory (0x239110) for 'i':


The following data types are all of value type:

  • bool
  • byte
  • char
  • decimal
  • double
  • enum
  • float
  • int
  • long
  • sbyte
  • short
  • struct
  • uint
  • ulong
  • ushort

Passing Value Type Variables

When you pass a value-type variable from one method to another, the system creates a separate copy of a variable in another method. If value got changed in the one method, it wouldn't affect the variable in another method.

Example: Passing Value Type Variable

static void ChangeValue(int x)
{
    x =  200;

    Console.WriteLine(x);
}

static void Main(string[] args)
{
    int i = 100;

    Console.WriteLine(i);
    
    ChangeValue(i);
    
    Console.WriteLine(i);
}

In the above example, variable i in the Main() method remains unchanged even after we pass it to the ChangeValue() method and change it's value there.

Reference Type

Unlike value types, a reference type doesn't store its value directly. Instead, it stores the address where the value is being stored. In other words, a reference type contains a pointer to another memory location that holds the data.

For example, consider the following string variable:

string s = "Hello World!!";

The following image shows how the system allocates the memory for the above string variable.


As you can see in the above image, the system selects a random location in memory (0x803200) for the variable s. The value of a variable s is 0x600000, which is the memory address of the actual data value. Thus, reference type stores the address of the location where the actual value is stored instead of the value itself.

The followings are reference type data types:

  • String
  • Arrays (even if their elements are value types)
  • Class
  • Delegate

Passing Reference Type Variables

When you pass a reference type variable from one method to another, it doesn't create a new copy; instead, it passes the variable's address. So, If we change the value of a variable in a method, it will also be reflected in the calling method.

Example: Passing Reference Type Variable

static void ChangeReferenceType(Student std2)
{
    std2.StudentName = "Steve";
}

static void Main(string[] args)
{
    Student std1 = new Student();
    std1.StudentName = "Bill";
    
    ChangeReferenceType(std1);

    Console.WriteLine(std1.StudentName);
}

In the above example, we pass the Student object std1 to the ChangeReferenceType() method. Here, it actually pass the memory address of std1. Thus, when the ChangeReferenceType() method changes StudentName, it is actually changing StudentName of std1 object, because std1 and std2 are both pointing to the same address in memory.

String is a reference type, but it is immutable. It means once we assigned a value, it cannot be changed. If we change a string value, then the compiler creates a new string object in the memory and point a variable to the new memory location. So, passing a string value to a function will create a new variable in the memory, and any change in the value in the function will not be reflected in the original value, as shown below.

Example: Passing String

static void ChangeReferenceType(string name)
{
    name = "Steve";
}

static void Main(string[] args)
{
    string name = "Bill";
    
    ChangeReferenceType(name);

    Console.WriteLine(name);
}

Null

The default value of a reference type variable is null when they are not initialized. Null means not refering to any object.

A value type variable cannot be null because it holds value, not a memory address.
C# 2.0 introduced nullable types, using which you can assign null to a value type variable or declare a value type variable without assigning a value to it.

 

Shallow Copy and Deep Copy Using C#


When we copy one instance to another using C# what happen is that both instances share the same memory address. But this is not the behavior we want most of the time.

When we create a copy of an object, for example:

MyClass obj=new MyClass()
MyClass obj2=obj;

Then the '=' operator copies the reference and not the object (and it works fine for a Value Type).

By default we get this behavior using the MemberwiseClone() method that is defined in the super class called System.Object. This is called “Shallow Copy”.

To get the same behavior for a Reference Type as well as a Value Type we use the Clone() method that belongs to the System.ICloneable interface. This is called a “Deep Copy”.

We will see both behaviors in depth one by one.

Shallow Copy

A Shallow Copy is about copying an object's value type fields into the target object and the object's reference types are copied as references into the target object but not the referenced object itself. It copies the types bit by bit. The result is that both instances are cloned and the original will refer to the same object.

We can get this behavior using MemberwiseClone() as mentioned earlier.

Now let's demonstrate this behavior using the following code:

class ShallowCopy

{

     public int I {get;set;}

     public int J {get;set;}

}

class Demo

{

public static void Main()

{

     ShallowCopy obj=new ShallowCopy();

     ShallowCopy objClone=obj;

    obj.I=10;// setting obj value after cloning..

     Console.WriteLine(“objvalue : {0} \t Clone value : {1}”,obj.I,objClone.I=10);

}

 

Output : obj value : 10 Clone value : 10

Surprise! This is not what we were looking for, right? So here the MemberwiseClone() method is useful. Let's change the class as in the following:

class ShallowCopy :  ICloneable

{

     public int I {get;set;}

     public int J {get;set;}

 

     //method for cloning object

     public object Clone()

    {

         return this.MemberwiseClone();

    }

}

class Demo

{

     public static void Main()

    {

         ShallowCopy obj=new ShallowCopy();

         Console.WriteLine(“--------before Shellow Clopy------”);

         ShallowCopy objClone=obj;

         obj.I=10;// setting obj value after cloning..

         Console.WriteLine(“objvalue : {0} \t Clone value : {1}”,obj.I,objClone.I=10);

         Console.WriteLine(“--------after Shellow Copy------”);

         ShallowCopy objClone2=(ShallowCopy)obj.Clone();  // cast object to //ShallowCopy

        obj.I=1000;  // MemberwiseClone() will not use this reference..

        Console.WriteLine(“after using MemberwiseClone() Clone() method :{0}”,objClone2.I);

    }
}


Output:

--------before Shellow Clopy------
obj value :10 Clone value :10

--------after Shellow Copy------
after using MemberwiseClone() Clone() method :10

Now it works as expected.

As per the above example the Clone() method is creating a true copy of an object and creating a new heap in memory for a cloned instance.

But what if we use a Refrence Type instead of primitive data types?

The answer is, for Reference Types, MeberwiseClone() does not clone an object.

In other words, if we use any reference type in the ShallowCopy class, for example:

Class ShallowCopy
{

   ...public String K {get;set;}
}

This behavior is like we are copying files and folders from one directory to another, but it's copying files and folders only, not the files that are there inside the folder.

Now to make this Clone() method for a complete copy (deep copy) let's explore the concept of Deep Copy.

Deep Copy

Deep Copy is used to make a complete deep copy of the internal reference types, for this we need to configure the object returned by MemberwiseClone().

In another words a deep copy occurs when an object is copied along with the objects to which it refers.

Let's understand it using the following code:

class ReferenceType

{

     public int RFT {  get set; }

}

class ShallowCopy :  ICloneable

{

     public int I {  get set; }

     public int J {  get set; }

     public ReferenceType K =  new ReferenceType();

 

     //Method updated for reference type ..

     public object Clone()

    {

         // Shalllow Copy..

         ShallowCopy SC = (ShallowCopy)this.MemberwiseClone();

 

         // Deep copy...

         ReferenceType RT =  new ReferenceType();

        RT.RFT =  this.K.RFT;

        SC.K = RT;

         return SC;

    }

     public static void Main(String[] args)

    {

         ShallowCopy obj =  new ShallowCopy();

        obj.K.RFT = 100;

         ShallowCopy objclone = (ShallowCopy)obj.Clone();

        obj.K.RFT = 200;  // make changes in obj.

         Console.WriteLine(objclone.K.RFT);

    }

}

Output : 100.

The code that is marked as bold shows how to configure a Deep Copy for a Reference Type to get the behavior we want.

Now in this case the clone object refers to a completely different copy along with a reference type.

Here is a simple image that describe both:



Apart from this I found a nice generic method for this, see the link below: