Online Earning Sources (Without Investment)

If you want to post, send your post on dotnetglobe@gmail.com .Put 'Title' as 'Subject'

Pages

Sunday, March 1, 2009

Implementing Caching in ASP.NET

What is Caching?

Caching is a technique of persisting the data in
memory for immediate access to requesting program calls. Many in the developer
community consider caching as one of the features available to improve
performance of Web applications.

Why Caching?

Consider a page that has list of Employee name, contact numbers and mail-Ids of
existing employees of a company on an intranet accessible by all employees.
This is very useful information that is available throughout the company and
could also be one of the most accessed pages. The functionality of adding,
updating or deleting is usually less intensive compared to more
transaction-based systems like Purchase ordering, Voucher creation etc. Now in
a normal scenario the process of querying database for each request is not
cost-effective in terms of server resources, hence is lot better to cache or
persist the data to avoid this costly loss of resources.

The .NET Advantage

ASP.NET provides the flexibility in terms of caching at different levels.
1. Page Level Output Caching

This is at the page level and one of the easiest means for caching pages.
This requires one to specify Duration of cache and Attribute of caching.

Syntax: <%@ OutputCache Duration=”60″ VaryByParam=”none” %>

The above syntax specifies that the page be cached for duration of 60
seconds and the value “none” for VaryByParam* attribute makes sure that there
is a single cached page available for this duration specified.

* VaryByParam can take various “key” parameter names in query string. Also

there are other attributes like VaryByHeader, VaryByCustom etc. Please refer to
MSDN for more on this.

2. Fragment Caching

Even though this definition refers to caching portion/s of page, it is

actually caching a user control that can be used in a base web form page. In
theory, if you have used include files in the traditional ASP model then this
caching model is like caching these include files separately. In ASP.NET more
often this is done through User Controls. Initially even though one feels a bit
misleading, this is a significant technique that can be used especially when
implementing “n” instances of the controls in various *.aspx pages. We can use
the same syntax that we declared for the page level caching as shown above, but
the power of fragment caching comes from the attribute “VaryByControl”. Using
this attribute one can cache a user control based on the properties exposed.

Syntax: <%@ OutputCache Duration=”60″ VaryByControl=”DepartmentId”
%>

The above syntax when declared within an *.ascx file ensures that the
control is cached for 60 seconds and the number of representations of cached
control is dependant on the property “DepartmentId” declared in the control.

Add the following into an *.ascx file. Please note the use of tag “Control”
and the cache declaration.

<%@ Control Language=”C#”%>

<%@ outputcache duration=”60″ varybycontrol=”DepartMentId” %>
<script runat=”server”>
private int _Departmentid=0;
public int DepartMentId
{
get{return _Departmentid;}
set{_Departmentid =value;}
}
//Load event of control
void Page_Load(Object sender, EventArgs e)
{
lblText.Text = “Time is ” + DateTime.Now.ToString() + ” for Department id = ”
+ _Departmentid + “\n”;
}
</script>
<asp:Label id=”lblText” runat=”server”></asp:Label>

Add the following to an *.aspx file. Please note the way “Register” tag is

used; the declaration of control using syntax <[TagPrefix]:[TagName]>;
Usage of property ” DepartMentId”. Open the page in two browsers and closely
watch the Base form timing and the User control timing. Also note that the
following page results in two copies or representation of user control in the
cache.

<%@ Page Language=”C#” Trace=”true” %>

<%@ Register TagPrefix=”CacheSample” TagName=”Text” Src=”CachingControl.ascx”
%>
<script runat=server>
void Page_Load(Object sender, EventArgs e)
{
this.lbltime.Text =”Base form time is ” + DateTime.Now.ToString() + “\n”;
}
</script>
<html>
<head>
</head>
<body>
<form runat=”server” ID=”Form2″>
<table>
<tbody>
<tr>
<td>
<asp:Label id=”lbltime” runat=”server”></asp:Label>
</td>
</tr>
<tr>
<td>
<CACHESAMPLE:TEXT id=”instance1″ runat=”Server”
DepartMentId=”0″>
</CACHESAMPLE:TEXT>
</td>
</tr>
<tr>
<td>
<CACHESAMPLE:TEXT id=”instance2″ runat=”Server”
DepartMentId=”1″>
</CACHESAMPLE:TEXT>
</td>
</tr>
</tbody>
</table>
</form>
</body>
</html>



3. Application Level Caching

With Page level Output caching one cannot cache objects between pages within

an application. Fragment caching is great in that sense but has limitations by
using user controls as means to do. We can use the Cache object
programmatically to take advantage of caching objects and share the same
between pages. Further the availability of different overloaded methods gives a
greater flexibility for our Cache policy like Timespan, Absolute expiration
etc. But one of the biggest takes is the CacheDependancy. This means that one
can create a cache and associate with it a dependency that is either another
cache key or a file.

In almost all Web applications there could be numerous master tables that

act as lookups to application specific tables. For e.g. if you take up adding a
Employee, usually one has master tables like “tblQualification” to get list of
qualifications, “tblLocations” to get list of locations etc. These tables* are
usually set during the initial application configuration phase and could be
modified once a month or even less than that. Hence it makes sense for us to
use them in our Cache rather than making calls to database on each request. But
then what Cache Policy do we adopt?

We cannot hold these objects in Cache for entire application instance,

because if anybody changes data in these tables one has to also refresh the
cache. It is here that CacheDependancy can be used.

* Even though these tables are less frequently used for updates, they are

extensively used in our select statements through out the applications.

Find below the snippet that uses CacheDependancy. Here what I have done is

to provide a list view of existing employees. You need to create a Database in
Sql Server, setup some data before you can continue. The schema scripts are
enclosed in the article.

Add database connection value in Web.Config and change the value as per your

setup.

<appSettings>

<add key=”conn” value=”Data Source=vishnu;trusted_connection=yes;Initial
Catalog=Users”/>
</appSettings>

First I get the dataset into which I fill the user list. But before this I

check for the cache initially if it exists I directly cast it to a dataset, if
not create a cache again.
daUsers.Fill(dsUsers,”tblUsers”);

I create the cache with “Users” as key using Cache.Insert* and link this

with a file “Master.xml”. This “Master.xml” is a XML file that contains Master
data of “tblQualifications” and “tbllocations”. I have used “Server.MapPath” to
get the physical path of the file on the server. The CacheDependancy instance
will make sure that any change in this dependency file means that you need to
recreate your cache key definition. This is a great feature to use since I can
recreate my cache only when required instead of caching the data at the page
level.

Cache.Insert(”Users”,dsUsers,new

System.Web.Caching.CacheDependency(Server.MapPath(”Master.xml”)) ,
DateTime.Now.AddSeconds(45),TimeSpan.Zero);

* For other overloaded parameters refer MSDN.

Also note how we could use trace within to add my own statements.

HttpContext.Current.Trace.Write(”from Database..”);

<%@ Page Language=”c#” Trace=”true” %>

<%@ import Namespace=”System” %>
<%@ import Namespace=”System.Data” %>
<%@ import Namespace=”System.Data.SqlClient” %>
<%@ import Namespace=”System.Configuration” %>
<%@ import Namespace=”System.Web” %>
<%@ import Namespace=”System.Collections” %>
<%@ import Namespace=”System.IO” %>
<script runat=”server”>
void Page_Load(Object sender, EventArgs e)
{
DataSet dsUsers;
try
{
if(Cache["Users"]==null)
{
SqlConnection cn;
dsUsers = new DataSet(”new”);
cn = new SqlConnection(ConfigurationSettings.AppSettings.Get(”conn”));
SqlDataAdapter daUsers;
daUsers = new SqlDataAdapter(”Select * from tblUsers”,cn);
cn.Open();
daUsers.Fill(dsUsers,”tblUsers”);
//Update the cache object
Cache.Insert(”Users”,dsUsers, new System.Web.Caching.CacheDependency(
Server.MapPath(”Master.xml”)), DateTime.Now.AddSeconds(45),TimeSpan.Zero);
HttpContext.Current.Trace.Write(DateTime.Now.AddSeconds(45).ToString() + ”
is expiry time..”);
cn.Close();
cn.Dispose();
HttpContext.Current.Trace.Write(”from Database..”);
lblChange.Text =”From the database….”;
}
else
{
HttpContext.Current.Trace.Write(”From cache..”);
lblChange.Text =”From the cache….”;
dsUsers= (DataSet) Cache["Users"];
}
dlUsers.DataSource =dsUsers;
dlUsers.DataMember = dsUsers.Tables[0].TableName ;
//lblChange.Text += Server.MapPath(”Master.xml”);
this.DataBind();
}
catch(Exception ex)
{
lblChange.Text = ex.Message;
}
}
</script>
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.0 Transitional//EN” >
<html>
<head>
<title>Cache Dependency Tester</title>
<meta content=”Microsoft Visual Studio 7.0″ name=”GENERATOR” />
<meta content=”C#” name=”CODE_LANGUAGE” />
<meta content=”JavaScript” name=”vs_defaultClientScript” />
<meta content=”http://schemas.microsoft.com/intellisense/ie5″
name=”vs_targetSchema” />
</head>
<body ms_positioning=”GridLayout”>
<form id=”Form1″ method=”post” runat=”server”>
<asp:DataList id=”dlUsers” style=”Z-INDEX: 101; LEFT: 44px; POSITION:
absolute; TOP: 104px” runat=”server” Height=”148px” Width=”343px”
BorderWidth=”1px” GridLines=”Horizontal” CellPadding=”4″
BackColor=”White” ForeColor=”Black” BorderStyle=”None”
BorderColor=”#CCCCCC”>
<SelectedItemStyle font-bold=”True” forecolor=”White”
backcolor=”#CC3333″></SelectedItemStyle>
<FooterStyle forecolor=”Black”
backcolor=”#CCCC99″></FooterStyle>
<HeaderStyle font-bold=”True” forecolor=”White”
backcolor=”#333333″></HeaderStyle>
<ItemTemplate>
<table>
<tr>
<td>
<%#DataBinder.Eval(Container.DataItem,”UserId”)%></td>
<td>
<%#DataBinder.Eval(Container.DataItem,”FirstName”)%></td>
<td>
<%#DataBinder.Eval(Container.DataItem,”LastName”)%></td>
</tr>
</table>
</ItemTemplate>
</asp:DataList>
<asp:Label id=”lblChange” style=”Z-INDEX: 102; LEFT: 46px; POSITION:
absolute; TOP: 63px” runat=”server” Height=”28px”
Width=”295px”></asp:Label>
<asp:Button id=”btnMaster” style=”Z-INDEX: 103; LEFT: 50px; POSITION:
absolute; TOP: 293px” onclick=”btnMaster_Click” runat=”server” Text=”Refresh
Master”></asp:Button>
</form>
</body>
</html>

We created the page that initiates and uses the Cache. For testing purpose

we need another page that will overwrite this “Master.xml” on click of a button
for which the code snippet is as follows. This ideally should be our master
maintenance page that adds/updates Master records in database and overwrites
the XML. But to make it easy I have just written an overwriting sample.

<%@ Page Language=”C#” Trace=”true”%>

<%@ import Namespace=”System” %>
<%@ import Namespace=”System.Data” %>
<%@ import Namespace=”System.Data.SqlClient” %>
<script runat=”server”>
void btnMaster_Click(Object sender, EventArgs e)
{
//Call save function
this.Save();
}
void Save()
{
try
{
SqlConnection cn;
DataSet dsUsers = new DataSet(”Users”);
//I have used this to get the Connectionstring from the
//Configuration file.
cn = new SqlConnection(ConfigurationSettings.AppSettings.Get(”conn”));
SqlDataAdapter daQualification;
SqlDataAdapter daLocations;
daQualification = new SqlDataAdapter(”Select * from tblqualifications”,cn);
daLocations = new SqlDataAdapter(”Select * from tblLocations”,cn);
cn.Open();
daQualification.Fill(dsUsers,”tblqualifications”);
daLocations.Fill(dsUsers,”tblLocations”);
HttpContext.Current.Trace.Write(”Masters data up..”);
//Overwrite the XML file. Also please read MSDN on the overloaded parameters
for WriteXml
dsUsers.WriteXml(HttpContext.Current.Server.MapPath
“Master.xml”),XmlWriteMode.WriteSchema);
cn.Close();
cn.Dispose();
}
catch(Exception ex)
{
throw new Exception(ex.Message);
}
}
</script>
<html>
<head>
</head>
<body>
<form runat=”server” ID=”Form1″>
<span>
<table>
<tbody>
<tr>
<td>
<label id=”lblRefresh” runat=”server”>
Rewrite the XML File by clicking the buttonbelow.</label>
</td>
</tr>
<tr align=”middle”>
<td>
<asp:Button id=”btnMaster” onclick=”btnMaster_Click” runat=”server”
Text=”Write XML”></asp:Button>
</td>
</tr>
</tbody>
</table>
</span>
</form>
</body>
</html>

Now once you have created the above pages i.e. one that implements caching

and other that overwrites the dependency file, create two instance of browser
and open the cache implementation page and note for trace, label text; open the
other instance of browser with the page which overwrites the XML. Note the
former, the first time it fetches data from the database and the subsequent
request will be from cache till your expiration time of 45 seconds is reached
or anyone overwrites or changes the “Master.xml” file. Also give a look on
Timespan parameter since you have a concept of Sliding expiration that can also
be implemented. Keep refreshing the first page and you will see that trace
indicates the cached page retrieval. Click the overwrite XML button on the
latter page that would overwrite the XML and again refresh the former page to
note that the data is retrieved from database. Though in this example I have
not shown any direct relation between the cached data and the dependency file
(like get values from dependency file and merge with cached object etc) in
terms of integrated usage, this could very easily be designed and implemented.
Dependency caching is a powerful technique that .NET supports and should be
utilized wherever applicable.

Conclusion

Caching is a technique that definitely improves the performance of web
applications if one is careful to have a balance in terms of which data needs
to be cached and parameter values for expiration policy.





Indexers

As you know, array indexing is performed using the [ ] operator. It is possible to overload the [ ] operator for classes that you create, but you don’t use an operator method. Instead, you create an indexer. An indexer allows an object to be indexed like an array. The main use of indexers is to support the creation of specialized arrays that are subject to one or more constraints. However, you can use an indexer for any purpose for which an array-like syntax is beneficial. Indexers can have one or more dimensions. We will begin with one-dimensional indexers.

Creating One-Dimensional Indexers

A one-dimensional indexer has this general form:

element-type this[int index] {
  // The get accessor.
  get {
    // return the value specified by index
  }
 
  // The set accessor.
  set {
    // set the value specified by index
  }
}

Here, element-type is the base type of the indexer. Thus, each element accessed by the indexer will be of type element-type. The element type corresponds to the base type of an array. The parameter index receives the index of the element being accessed. Technically, this parameter does not have to be of type int, but since indexers are typically used to provide array indexing, an integer type is customary.

Inside the body of the indexer are defined two accessors that are called get and set. An accessor is similar to a method except that it does not have a return type or parameter declarations. The accessors are automatically called when the indexer is used, and both accessors receive index as a parameter. If the indexer is on the left side of an assignment statement, then the set accessor is called, and the element specified by index must be set. Otherwise, the get accessor is called, and the value associated with index must be returned. The set accessor also receives an implicit parameter called value, which contains the value being assigned to the specified index.

One of the benefits of an indexer is that you can control precisely how an array is accessed, heading off improper accesses. Here is an example. In the following program the FailSoftArray class implements an array that traps boundary errors, thus preventing runtime exceptions if the array is indexed out-of-bounds. This is accomplished by encapsulating the array as a private member of a class, allowing access to the array only through the indexer. With this approach, any attempt to access the array beyond its boundaries can be prevented, with such an attempt failing gracefully (resulting in a “soft landing” rather than a “crash”). Since FailSoftArray uses an indexer, the array can be accessed using the normal array notation:

// Use an indexer to create a fail-soft array.
 
using System;
 
class FailSoftArray {
  int[] a;    // reference to underlying array
 
  public int Length; // Length is public
 
  public bool errflag; // indicates outcome of last operation
 
  // Construct array given its size.
  public FailSoftArray(int size) {
    a = new int[size];
    Length = size;
  }
 
  // This is the indexer for FailSoftArray.
  public int this[int index] {
    // This is the get accessor.
    get {
      if(ok(index)) {
        errflag = false;
        return a[index];
      } else {
        errflag = true;
        return 0;
      }
    }
    // This is the set accessor
    set {
      if(ok(index)) {
        a[index] = value;
        errflag = false;
      }
      else errflag = true;
    }
  }
 
  // Return true if index is within bounds.
  private bool ok(int index) {
   if(index >= 0 & index <>
   return false;
  }
}
 
// Demonstrate the fail-soft array.
class FSDemo {
  public static void Main() {
    FailSoftArray fs = new FailSoftArray(5);
    int x;
 
    // show quiet failures
    Console.WriteLine("Fail quietly.");
    for(int i=0; i < (fs.Length * 2); i++)
      fs[i] = i*10;
 
    for(int i=0; i < (fs.Length * 2); i++) {
      x = fs[i];
      if(x != -1) Console.Write(x + " ");
    }
    Console.WriteLine();
 
    // now, generate failures
    Console.WriteLine("\nFail with error reports.");
    for(int i=0; i < (fs.Length * 2); i++) {
      fs[i] = i*10;
      if(fs.errflag)
        Console.WriteLine("fs[" + i + "] out-of-bounds");
    }
 
    for(int i=0; i < (fs.Length * 2); i++) {
      x = fs[i];
      if(!fs.errflag) Console.Write(x + " ");
      else
        Console.WriteLine("fs[" + i + "] out-of-bounds");
    }
  }
}

The output from the program is shown here:

Fail quietly.
0 10 20 30 40 0 0 0 0 0
 
Fail with error reports.
fs[5] out-of-bounds
fs[6] out-of-bounds
fs[7] out-of-bounds
fs[8] out-of-bounds
fs[9] out-of-bounds
0 10 20 30 40 fs[5] out-of-bounds
fs[6] out-of-bounds
fs[7] out-of-bounds
fs[8] out-of-bounds
fs[9] out-of-bounds

The indexer prevents the array boundaries from being overrun. Let’s look closely at each part of the indexer. It begins with this line:

public int this[int index] {

This declares an indexer that operates on int elements. The index is passed in index. The indexer is public, allowing it to be used by code outside of its class.

The get accessor is shown here:

get {
  if(ok(index)) {
    errflag = false;
    return a[index];
  } else {
    errflag = true;
    return 0;
  }
}

The get accessor prevents array boundary errors by first confirming that the index is not out-of-bounds. This range check is performed by the ok( ) method, which returns true if the index is valid, and false otherwise. If the specified index is within bounds, the element corresponding to the index is returned. If it is out of bounds, no operation takes place and no overrun occurs. In this version of FailSoftArray, a variable called errflag contains the outcome of each operation. This field can be examined after each operation to assess the success or failure of the operation.

The set accessor is shown here. It too prevents a boundary error.

set {
  if(ok(index)) {
    a[index] = value;
    errflag = false;
  }
  else errflag = true;
}

Here, if index is within bounds, the value passed in value is assigned to the corresponding element. Otherwise, errflag is set to true. Recall that in an accessor method, value is an implicit parameter that contains the value being assigned. You do not need to (nor can you) declare it.

Indexers do not have to support both get and set. You can create a read-only indexer by implementing only the get accessor. You can create a write-only indexer by implementing only set.

Indexers Can Be Overloaded

An indexer can be overloaded. The version executed will be the one that has the closest type-match between its parameter and the argument used as an index. Here is an example that overloads the FailSoftArray indexer for indexes of type double. The double indexer rounds its index to the nearest integer value.

// Overload the FailSoftArray indexer.
 
using System;
 
class FailSoftArray {
  int[] a;    // reference to underlying array
 
  public int Length; // Length is public
 
  public bool errflag; // indicates outcome of last operation
 
  // Construct array given its size.
  public FailSoftArray(int size) {
    a = new int[size];
    Length = size;
  }
 
  // This is the int indexer for FailSoftArray.
  public int this[int index] {
    // This is the get accessor.
    get {
      if(ok(index)) {
        errflag = false;
        return a[index];
      } else {
        errflag = true;
        return 0;
      }
    }
 
    // This is the set accessor
    set {
      if(ok(index)) {
        a[index] = value;
        errflag = false;
      }
      else errflag = true;
    }
  }
  /* This is another indexer for FailSoftArray.
     This index takes a double argument.  It then
     rounds that argument to the nearest integer
     index. */
  public int this[double idx] {
    // This is the get accessor.
    get {
      int index;
 
      // round to nearest int
      if( (idx - (int) idx) < index =" (int)">
      else index = (int) idx + 1;
 
      if(ok(index)) {
        errflag = false;
        return a[index];
      } else {
        errflag = true;
        return 0;
      }
    }
 
    // This is the set accessor
    set {
      int index;
 
      // round to nearest int
      if( (idx - (int) idx) < index =" (int)">
      else index = (int) idx + 1;
 
      if(ok(index)) {
        a[index] = value;
        errflag = false;
      }
      else errflag = true;
    }
  }
 
  // Return true if index is within bounds.
  private bool ok(int index) {
   if(index >= 0 & index <>
   return false;
  }
}
 
// Demonstrate the fail-soft array.
class FSDemo {
  public static void Main() {
    FailSoftArray fs = new FailSoftArray(5);
 
    // put some values in fs
    for(int i=0; i <>
      fs[i] = i;
    // now index with ints and doubles
    Console.WriteLine("fs[1]: " + fs[1]);
    Console.WriteLine("fs[2]: " + fs[2]);
 
    Console.WriteLine("fs[1.1]: " + fs[1.1]);
    Console.WriteLine("fs[1.6]: " + fs[1.6]);
 
  }
}

This program produces the following output:

fs[1]: 1
fs[2]: 2
fs[1.1]: 1
fs[1.6]: 2

As the output shows, the double indexes are rounded to their nearest integer value. Specifically, 1.1 is rounded to 1, and 1.6 is rounded to 2.

Although overloading an indexer as shown in this program is valid, it is not common. Most often, an indexer is overloaded to enable an object of a class to be used as an index, with the index computed in some special way.

Indexers Do Not Require an Underlying Array

It is important to understand that there is no requirement that an indexer actually operate on an array. It simply must provide functionality that appears “array-like” to the user of the indexer. For example, the following program has an indexer that acts like a read-only array that contains the powers of 2 from 0 to 15. Notice, however, that no actual array exists. Instead, the indexer simply computes the proper value for a given index.

// Indexers don't have to operate on actual arrays.
 
using System;
 
class PwrOfTwo {
 
  /* Access a logical array that contains
     the powers of 2 from 0 to 15. */
  public int this[int index] {
    // Compute and return power of 2.
    get {
      if((index >= 0) && (index <>
      else return -1;
    }
 
    // there is no set accessor
  }
 
  int pwr(int p) {
    int result = 1;
    for(int i=0; i <>
      result *= 2;
 
    return result;
  }
}
 
class UsePwrOfTwo {
  public static void Main() {
    PwrOfTwo pwr = new PwrOfTwo();
 
    Console.Write("First 8 powers of 2: ");
    for(int i=0; i <>
      Console.Write(pwr[i] + " ");
    Console.WriteLine();
 
    Console.Write("Here are some errors: ");
    Console.Write(pwr[-1] + " " + pwr[17]);
 
    Console.WriteLine();
  }
}

The output from the program is shown here:

First 8 powers of 2: 1 2 4 8 16 32 64 128
Here are some errors: -1 -1

Notice that the indexer for PwrOfTwo includes a get accessor, but no set accessor. As explained, this means that the indexer is read-only. Thus, a PwrOfTwo object can be used on the right side of an assignment statement, but not on the left. For example, attempting to add this statement to the preceding program won’t work:

pwr[0] = 11; // won't compile

This statement will cause a compilation error because there is no set accessor defined for the indexer.

There are two important restrictions to using indexers. First, because an indexer does not define a storage location, a value produced by an indexer cannot be passed as a ref or out parameter to a method. Second, an indexer must be an instance member of its class; it cannot be declared static.

Multidimensional Indexers

You can create indexers for multidimensional arrays, too. For example, here is a two-dimensional fail-soft array. Pay close attention to the way that the indexer is declared.

// A two-dimensional fail-soft array.
 
using System;
 
class FailSoftArray2D {
  int[,] a; // reference to underlying 2D array
  int rows, cols; // dimensions
  public int Length; // Length is public
 
  public bool errflag; // indicates outcome of last operation
 
  // Construct array given its dimensions.
  public FailSoftArray2D(int r, int c) {
    rows = r;
    cols = c;
    a = new int[rows, cols];
    Length = rows * cols;
  }
 
  // This is the indexer for FailSoftArray2D.
  public int this[int index1, int index2] {
    // This is the get accessor.
    get {
      if(ok(index1, index2)) {
        errflag = false;
        return a[index1, index2];
      } else {
        errflag = true;
        return 0;
      }
    }
 
    // This is the set accessor.
    set {
      if(ok(index1, index2)) {
        a[index1, index2] = value;
        errflag = false;
      }
      else errflag = true;
    }
  }
 
  // Return true if indexes are within bounds.
  private bool ok(int index1, int index2) {
   if(index1 >= 0 & index1 <>
      index2 >= 0 & index2 <>
         return true;
 
   return false;
  }
}
 
// Demonstrate a 2D indexer.
class TwoDIndexerDemo {
  public static void Main() {
    FailSoftArray2D fs = new FailSoftArray2D(3, 5);
    int x;
    // show quiet failures
    Console.WriteLine("Fail quietly.");
    for(int i=0; i <>
      fs[i, i] = i*10;
 
    for(int i=0; i <>
      x = fs[i,i];
      if(x != -1) Console.Write(x + " ");
    }
    Console.WriteLine();
 
    // now, generate failures
    Console.WriteLine("\nFail with error reports.");
    for(int i=0; i <>
      fs[i,i] = i*10;
      if(fs.errflag)
        Console.WriteLine("fs[" + i + ", " + i + "] out-of-bounds");
    }
 
    for(int i=0; i <>
      x = fs[i,i];
      if(!fs.errflag) Console.Write(x + " ");
      else
        Console.WriteLine("fs[" + i + ", " + i + "] out-of-bounds");
    }
  }
}

The output from this program is shown here:

Fail quietly.
0 10 20 0 0 0
 
Fail with error reports.
fs[3, 3] out-of-bounds
fs[4, 4] out-of-bounds
fs[5, 5] out-of-bounds
0 10 20 fs[3, 3] out-of-bounds
fs[4, 4] out-of-bounds
fs[5, 5] out-of-bounds

Properties in .Net

Another type of class member is the property. A property combines a field with the methods that access it. As some examples earlier in this book have shown, often you will want to create a field that is available to users of an object, but you want to maintain control over the operations allowed on that field. For instance, you might want to limit the range of values that can be assigned to that field. While it is possible to accomplish this goal through the use of a private variable along with methods to access its value, a property offers a better, more streamlined approach.
Properties are similar to indexers. A property consists of a name along with get and set accessors. The accessors are used to get and set the value of a variable. The key benefit of a property is that its name can be used in expressions and assignments like a normal variable, but in actuality the get and set accessors are automatically invoked. This is similar to the way that an indexer’s get and set accessors are automatically used.
The general form of a property is shown here:
type name {
get {
// get accessor code
}

set {
// set accessor code
}
}
Here, type specifies the type of the property, such as int, and name is the name of the property. Once the property has been defined, any use of name results in a call to its appropriate accessor. The set accessor automatically receives a parameter called value that contains the value being assigned to the property.
It is important to understand that properties do not define storage locations. Thus, a property manages access to a field. It does not, itself, provide that field. The field must be specified independently of the property.
Here is a simple example that defines a property called MyProp, which is used to access the field prop. In this case, the property allows only positive values to be assigned.
// A simple property example.

using System;

class SimpProp {
int prop; // field being managed by MyProp

public SimpProp() { prop = 0; }

/* This is the property that supports access to
the private instance variable prop. It
allows only positive values. */
public int MyProp {
get {
return prop;
}
set {
if(value >= 0) prop = value;
}
}
}

// Demonstrate a property.
class PropertyDemo {
public static void Main() {
SimpProp ob = new SimpProp();

Console.WriteLine("Original value of ob.MyProp: " + ob.MyProp);
ob.MyProp = 100; // assign value
Console.WriteLine("Value of ob.MyProp: " + ob.MyProp);

// Can't assign negative value to prop
Console.WriteLine("Attempting to assign -10 to ob.MyProp");
ob.MyProp = -10;
Console.WriteLine("Value of ob.MyProp: " + ob.MyProp);
}
}

Output from this program is shown here:
Original value of ob.MyProp: 0
Value of ob.MyProp: 100
Attempting to assign -10 to ob.MyProp
Value of ob.MyProp: 100

Let’s examine this program carefully. The program defines one private field, called prop, and a property called MyProp that manages access to prop. As explained, a property by itself does not define a storage location. A property simply manages access to a field. Thus, there is no concept of a property without an underlying field. Furthermore, because prop is private, it can be accessed only through MyProp.

The property MyProp is specified as public so that it can be accessed by code outside of its class. This makes sense because it provides access to prop, which is private. The get accessor simply returns the value of prop. The set accessor sets the value of prop if and only if that value is positive. Thus, the MyProp property controls what values prop can have. This is the essence of why properties are important.

The type of property defined by MyProp is called a read-write property because it allows its underlying field to be read and written. It is possible, however, to create read-only and write-only properties. To create a read-only property, define only a get accessor. To define a write-only property, define only a set accessor.
You can use a property to further improve the fail-soft array class. As you know, all arrays have a Length property associated with them. Up to now, the FailSoftArray class simply used a public integer field called Length for this purpose. This is not good practice, though, because it allows Length to be set to some value other than the length of the fail-soft array. (For example, a malicious programmer could intentionally corrupt its value.) We can remedy this situation by transforming Length into a read-only property, as shown in the following version of FailSoftArray:
// Add Length property to FailSoftArray.

using System;

class FailSoftArray {
int[] a; // reference to underlying array
int len; // length of array -- underlies Length property

public bool errflag; // indicates outcome of last operation

// Construct array given its size.
public FailSoftArray(int size) {
a = new int[size];
len = size;
}

// Read-only Length property.
public int Length {
get {
return len;
}
}

// This is the indexer for FailSoftArray.
public int this[int index] {
// This is the get accessor.
get {
if(ok(index)) {
errflag = false;
return a[index];
}
else {
errflag = true;
return 0;
}
}

// This is the set accessor
set {
if(ok(index)) {
a[index] = value;
errflag = false;
}
else errflag = true;
}
}

// Return true if index is within bounds.
private bool ok(int index) {
if(index >= 0 & index < fs =" new" i="0;" i="0;" x =" fs[i];" length =" 10;" length =" 10;" a =" new" len =" size;" errflag =" false;" errflag =" true;" errflag =" false;" errflag =" true;">= 0 & index < Length) return true;
return false;
}
}

// Demonstrate the improved fail-soft array.
class FinalFSDemo {
public static void Main() {
FailSoftArray fs = new FailSoftArray(5);

// use Error property
for(int i=0; i < fs.Length + 1; i++) {
fs[i] = i*10;
if(fs.Error)
Console.WriteLine("Error with index " + i);
}

}
}
The creation of the Error property has caused two changes to be made to FailSoftArray. First, errflag has been made private because it is now used as the underlying storage for the Error property. Thus, it won’t be available directly. Second, the read-only Error property has been added. Now, programs that need to detect errors will interrogate Error. This is demonstrated in Main( ), where a boundary error is intentionally generated, and the Error property is used to detect it.
Property Restrictions
Properties have some important restrictions. First, because a property does not define a storage location, it cannot be passed as a ref or out parameter to a method. Second, you cannot overload a property. (You can have two different properties that both access the same variable, but this would be unusual.) Finally, a property should not alter the state of the underlying variable when the get accessor is called. Although this rule is not enforced by the compiler, violating it is wrong. A get operation should be nonintrusive.

itworld