Saturday, November 13, 2010

How to: Implement a Lightweight Class with Auto-Implemented Properties


in my previous article i have discussed about Auto implemented properties. In this example I will shows how we can create an immutable lightweight class which will serve the purpose only to encapsulate a set of auto-implemented properties. We can use this kind of construct in place of a struct when we must use reference type semantics.
Note that in case of auto-implemented properties, both a get and set accessor are required. We make the class immutable by declaring the set accessors as private(Yes this is possible in C#). However, declaring a private set accessor, we cannot use an object initializer to initialize the property. For this we must use a constructor or a factory method.
Example
The following example shows two ways to implement an immutable class that has auto-implemented properties. The first class uses a constructor to initialize the properties, and the second class uses a static factory method.
//ClassA
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LightweightClass
{
    // This is an immutable class. Once an object is created, it cannot be modified outside the class. It only uses its constructor to initialize properties.
    class ClassA
    {
        // Read-only properties.
        public string Property1 { get; private set; }
        public string Property2 { get; private set; }

        // Public constructor.
        public ClassA(string Value1, string Value2)
        {
            Property1 = Value1;
            Property2 = Value2;
        }
    }
}


//ClassB
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LightweightClass
{
    // This is an immutable class. Once an object is created, it cannot be modified outside the class. It only uses its constructor or the public Factory Mthod to initialize properties.
    public class ClassB
    {
        // Read-only properties.
        public string Property1 { get; private set; }
        public string Property2 { get; private set; }

        // Private constructor.
        private ClassB(string Prop1, string Prop2)
        {
            Property1 = Prop1;
            Property2 = Prop2;
        }

        // Public factory method.
        public static ClassB CreateObject(string prop1, string prop2)
        {
            return new ClassB(prop1, prop2);
        }
    }


}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LightweightClass
{
    public class Program
    {
        static void Main()
        {
            // Some simple data sources.
            string[] names = {"Meetu Choudhary","Gaurav Arora", "Prabhjeet Gill",
                              "Nikita Rathi", "Manpreet Kaur"};
            string[] addresses = {"29 Jaipur", "12 Gaziabad.", "678 1st Ave",
                                  "12 Tonk", "89 Et. Mumabi"};

            // Simple query to demonstrate object creation in select clause.
            // Create objects of ClassA by using a constructor.
            var query1 = from i in Enumerable.Range(0, 5)
                         select new ClassA(names[i], addresses[i]);

            // List elements cannot be modified by client code.
            var list = query1.ToList();
            Console.WriteLine("Using Constroctor");
            foreach (var contact in list)
            {
                Console.WriteLine("{0}, {1}", contact.Property1 , contact.Property2 );
            }

            // Create ClassB objects by using a static factory method.
            var query2 = from i in Enumerable.Range(0, 5)
                         select ClassB.CreateObject(names[i], addresses[i]);

            // Console output is identical to query1.
            var list2 = query2.ToList();
            Console.WriteLine("Using Factory Method");
            foreach (var contact in list2)
            {
                Console.WriteLine("{0}, {1}", contact.Property1, contact.Property2);
            }
            // List elements cannot be modified by client code. this will gentrate the error
            // CS0272:
            // list2[0].Name = "Eugene Zabokritski";

            // Keep the console open in debug mode. to check
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }
}

Output

Using Constroctor
Meetu Choudhary, 29 Jaipur
Gaurav Arora, 12 Gaziabad.
Prabhjeet Gill, 678 1st Ave
Nikita Rathi, 12 Tonk
Manpreet Kaur, 89 Et. Mumabi
Using Factory Method
Meetu Choudhary, 29 Jaipur
Gaurav Arora, 12 Gaziabad.
Prabhjeet Gill, 678 1st Ave
Nikita Rathi, 12 Tonk
Manpreet Kaur, 89 Et. Mumabi
Press any key to exit.

Auto Implemented Properties


Auto Implemented Properties
In C# 3.0 and later, We have an easy, interesting way for declaring properties known as auto-implemented properties, These Auto implemented properties are especially used when there is no additional logic required in the property accessors. When a property is declared as using Auto Implementation as shown in the example below, compiler creates a private, anonymous backing field or you can say it a member which can be only accessed through the property's get and set accessors.

Example


The following example shows a simple class that has some auto-implemented properties:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AutoImplemented_Properties
{
    // This class is mutable. Its data can be modified from
    // outside the class.
    class Student
    {
        // Auto-Impl Properties for trivial get and set
        public double TotalMarks { get; set; }
        public string StudentName { get; set; }
        public int RollNo { get; set; }

        // Constructor
        public Student(double marks, string name, int Rno)
        {
            TotalMarks = marks;
            StudentName = name;
            RollNo = Rno;
        }
        // Methods
        public string GetStudentInfo()
        {
            return "Student Information : " + StudentName + " " + RollNo.ToString() + " " + TotalMarks.ToString();
        }
        public string GetMarks()
        {
            return "Marks : " + TotalMarks.ToString();
        }

        // .. Additional methods, events, etc.
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AutoImplemented_Properties
{
    class Program
    {
        static void Main(string[] args)
        {
            // Intialize a new object.
            Student Stud1 = new Student(987.50, "Meetu Choudhary", 90108);
            Console.WriteLine("Marks Before Modification");
            Console.WriteLine(Stud1.GetMarks());
            //Modify a property
            Stud1.TotalMarks += 499.99;
            Console.WriteLine("Marks After Modification");
            Console.WriteLine(Stud1.GetMarks());
            Console.WriteLine("Complete information of Student");
            Console.WriteLine(Stud1.GetStudentInfo());
            Console.ReadLine();
        }
    }
}

Output
Marks Before Modification
Marks : 987.5
Marks After Modification
Marks : 1487.49
Complete information of Student
Student Information : Meetu Choudhary 90108 1487.49


Code Discussion
  • Auto implemented properties are declared using the following line:
public double TotalMarks { get; set; }

·         Value of the property is increased by the object as  it is an Mutable class.

Stud1.TotalMarks += 499.99;

Note:
The class that is shown in the above example is mutable i.e. Client code has the power to change the values of objects after they are created. However, in complex classes that contain significant behavior (methods) and data, it might be a necessary to have public properties. but, for small classes or structs that may just encapsulate a set of values (data) with little or no behaviors (or Methods), it is recommended to make the objects which are immutable by declaring the set accessor as private. Attributes are permitted on auto-implemented properties but not on the backing fields as these fields are not accessible from our source code. If it is necessary to use an attribute on the backing field of a property, then just create a regular property instead of auto implemented property. For more information, see
How to: Implement a Lightweight Class with Auto-Implemented Properties 
To know about properties in C# read this article
For reading on Abstract Properties click here

Abstract Properties


Please read my previous article on Properties

Now, let’s move on to the second example to demonstrate how to define abstract properties.
Few things to keep in mind while using abstract properties
1.   It does not provide an implementation of the property accessor
2.   It has to be override in subclasses.
Example 2
In this example I am making three files:
  • abstractshape.cs: The abstract Shape class that contains an abstract Perimeter property.
  • shapes.cs: The subclasses of the Shape class.
  • shapetest.cs: A test program to display the areas of some Shape-derived objects.

File 1 - abstractshape.cs
This file declares the Shape class that contains the Perimeter property of the type double

// abstractshape.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    public abstract class Shape
    {
        private int myId;

        //Creating Constroctor with argument to set the id property
        public Shape(int shapeid)
        {
            Id = shapeid;   // calling the set accessor of the Id property
        }

        //craeting Id property for the shape class
        public int Id
        {
            get
            {
                return myId;
            }

            set
            {
                myId = value;
            }
        }

        // Perimeter is a read-only property - only a get accessor is needed:
        public abstract double Perimeter
        {
            get;
        }
        //overriding tostring() method
        public override string ToString()
        {
            return string.Format("{0}", Id) + " Perimeter = " + string.Format("{0:F2}", Perimeter);
        }
    }
}


Code Discussion
  • Property declaration itself contains the modifier, for example:
public abstract double Perimeter

  • While declaring an abstract property (in our example perimeter), we only need to indicate what will be the property accessors, without their implementation. In this example, only a Get accessor is available, so the property is read-only.

File 2 - shapes.cs
The following code shows three subclasses of Shape and how they override the Perimeter property to provide their own implementation.

// shapes.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    public class Square : Shape
    {
        private int Side;

        public Square(int side, int id)
            : base(id)
        {
            Side = side;
        }

        public override double Perimeter
        {
            get
            {
                // Given the side, return the Perimeter of a square:
                return 4 * Side;
            }
        }
    }

    public class Circle : Shape
    {
        private int myRadius;

        public Circle(int radius, int id)
            : base(id)
        {
            myRadius = radius;
        }

        public override double Perimeter
        {
            get
            {
                // Given the radius, return the Perimeter of a circle:
                return 2 * System.Math.PI * myRadius;
            }
        }
    }

    public class Rectangle : Shape
    {
        private int mylength;
        private int mybreadth;

        public Rectangle(int length, int breadth, int id)
            : base(id)
        {
            mylength = length;
            mybreadth = breadth;
        }

        public override double Perimeter
        {
            get
            {
                // Given the length and breadth, return the Perimeter of a rectangle:
                return 2 * mylength * mybreadth;
            }
        }
    }

}
Code Discussion
  • Shape class is Inherited in all three classes Rectangle, circle, square and id is passed to the base class constructor:
public Rectangle(int length, int breadth, int id)
            : base(id)

  • Each Class extending, implementing or inheriting the shape class has overridden and implemented the perimeter property which was read only as only get assessor was set. According to their own needs.
public override double Perimeter
        {
            get
            {
                // Given the length and breadth, return the Perimeter of a rectangle:
                return 2 * mylength * mybreadth;
            }
        }


File 3 - shapetest.cs
This file will contain a test class in which we will implement the classes we have created above and will check their results. By creating no of shape derived objects and calculating their perimeters

// shapetest.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class shapetest
    {
        public static void Main()
        {
            //creating an array of shape class
            Shape[] shapes =
         {
             //with an object of each subclass
            new Square(5, 1),
            new Circle(3, 2),
            new Rectangle( 4, 5, 3)
         };

            //writing on the console
            Console.WriteLine("Shapes Collection");
            //going through each object in shape class
            foreach (Shape s in shapes)
            {
                //as we have overriddin the tostring property so we can just write the object in the console.writeline
                Console.WriteLine(s);               
            }
            //waiting for user to press a key so that the screen (console) will be displayed till user press any key
            Console.ReadKey();
        }

    }
}



Output
Shapes Collection
1 Perimeter = 20.00
2 Perimeter = 18.85
3 Perimeter = 40.00



Code Discussion
  • An array of Shape class is created with 3 objects each of 3 different subclasses
Shape[] shapes =
         {
            new Square(5, 1),
            new Circle(3, 2),
            new Rectangle( 4, 5, 3)
         };
·         And then the perimeter property is used by tostring() of shape object to display on the console. Which is used in foreach loop to iterate through all shape objects?

foreach (Shape s in shapes)
            {
         
      Console.WriteLine(s);               
            }


Building and Running the Sample within Visual Studio
To build and run the Properties samples
  1. Open the solution.
  2. In Solution Explorer, right-click the project properties and click Application tab
  3. From Startup object select the shapetest as StartUp class.
  4. From the Debug menu, click Start without Debugging.
  5. Repeat the preceding steps for car class in example one.

Subscribe via email

Enter your email address:

Delivered by FeedBurner

MSDotnetMentor

MSDotnetMentor My Website http://msdotnetmentor.com