-4-
In the earlier chapters, we had
exploited the existing C# classes like String and int, which were utilized to
display text and values. In this chapter, we shall focus on creating our own
classes.
a.aspx
<%@ Inherits="zzz" Src="b1.cs" language=C#
%>
b1.cs
public class zzz
{
}
Output
Parser Error Message: 'zzz' is not a valid base class because
it does not extend class 'System.Web.UI.Page'.
So far, we have learnt about the
'language' attribute with the ASP directive, which is used to specify the
programming language that we shall be using in the file. The 'language'
attribute was initialized to C#, thereby signifying that the rules of C# will
be applicable on the code inserted with the aspx tags.
We now introduce two more
attributes in addition to the 'language' directive.
• inherits - This attribute requires
the name of a class and hence, we have provided the name zzz.
• src - This attribute is initialized
to b1.cs, which is a program file that contains the class.
A class is a collection of
variables and methods, which have been bunched together. It can therefore be
considered as a container of things.
The file b1.cs is a C# file which
contains the syntax 'public class zzz', followed by a pair of curly braces. The
keyword 'class' results in the creation of a class called zzz. The curly braces
signify the beginning and end of a class. Currently, the class that we have
created does not contain any variables or methods.
Thus, we can conclude that the
integer class was created through the syntax 'class int', while the string
class was created through the use of the syntax 'class string'. There is no
other procedure available, to create a class. The rules that apply to one class
are common to the rest of the classes in C#. All the code in the C# language is
contained in classes only.
In the above program, an error is
generated, since ASP+ does not expect an empty class. It expects the class to
contain variables and functions that it needs to invoke. However, they are not
currently present in the class. Since there is a paucity of time, and moreover,
due to lack of perseverance that is
vital for writing all this code ourselves, we shall borrow code from an
existing class that contains the relevant functions and variables.
To inherit or borrow from another
class, we have to make one small amendment to the source code in the file
b1.cs.
b1.cs
public class zzz : System.Web.UI.Page
{
}
There exists a free class called
System.Web.UI.Page that has been written by the programmers at Microsoft. This
class has all the functions that ASP+ requires. To inherit or borrow from an
existing class, we are required to place a colon : after the name of the class
(as in zzz), and then specify the class that we want to inherit from, i.e. System.Web.UI.Page. You may have noticed
that, for the Page class, the name System.Web.UI.Page is an extended one. We
shall look into the reasons for the same, a little later.
Technically, we can say that the
class zzz derives from class Page. This implies that all the methods and
variables of class Page are now available to class zzz also. In a sense, it is
akin to writing all the methods and variables in the zzz class, by ourselves.
When we derive from the class Page, whatever is contained in the class, also
exists in the derived class zzz, making it a rich class.
The word 'public', which precedes
the class name, is an access modifier. Access modifiers are used to restrict
access to a class, since it may not be desirable to allow everyone to access
the class. The access modifier 'public' facilitates anyone and everyone to
access the class. On making these changes to the source code, no errors are
generated when we run our program.
a.aspx
<%@ Inherits="zzz" Src="b1.cs" language=C#
%>
<form action="a.aspx" runat="server">
<asp:button type=submit text="Click"
OnClick="abc" runat="server"/>
</form>
b1.cs
public class zzz : System.Web.UI.Page
{
public void abc(System.Object a, System.EventArgs e)
{
Response.Write("hi");
}
}
After every click on the button,
you will see the word 'hi' displayed. Let us now understand how the button
works. Each time we click on a button, the function abc is called, since it is
associated with the attribute OnClick.
You will not notice words such
as, 'script' in the aspx file because the function code is not present in
a.aspx. It is located in the class zzz, which is present within the src file
named b1.cs. As you must have noticed, this function resides within the class
zzz.
The 'inherits' attribute makes
the code, which is present in the class zzz of the file b1.cs, accessible to
the aspx file. Now, the function abc does not generate any error, since it
meets its match and displays 'hi' in the browser window.
This is fascinating because it demonstrates
that all the code within a C# file, can now be used with ASP+. So, let us now
expand our horizons about the basics of C# programming. We shall leave a.aspx
untouched for a long while, and modify the file b1.cs progressively, to explain
certain issues related to the C# programming language.
b1.cs
public class zzz : System.Web.UI.Page
{
public void abc(System.Object o, System.EventArgs e)
{
yyy a;
}
}
Output
Compiler Error Message: CS0246: The type or namespace name
'yyy' could not be found (are you missing a using directive or an assembly
reference?)
In b1.cs, we are trying to create
an object 'a', that is an instance of a class yyy. But, the output displays an
error, stating that C# is unaware of the class yyy.
The occurrence of this error is
understandable, because C# may be well informed about, and familiar with
classes such as Page and a vast number of built-in classes, but it definitely
is not familiar with a user-defined class called yyy.
b1.cs
public class zzz : System.Web.UI.Page
{
public void abc(System.Object o, System.EventArgs e)
{
yyy a;
}
}
public class yyy
{
}
You may be able to recall what we
had mentioned a little while ago, that, the only way to create a class is by
using the keyword 'class', followed by the class name. It cannot get simpler
than this. Thus, no more errors are generated.
b1.cs
public class zzz : System.Web.UI.Page
{
public void abc(System.Object o, System.EventArgs e)
{
yyy a;
a.pqr();
}
}
public class yyy
{
public void pqr()
{
Response.Write("hell");
}
}
Output
Compiler Error Message: CS0165: Use of unassigned local
variable 'a'
It is not a child's play to learn
a programming language, especially when it also entails deciphering cryptic
errors. What does the error message 'unassigned local variable' signify? If you
may recall, in chapter 2, we had created a variable i of type int, and called
the ToString function off it. In a similar manner, we have now created a
variable a of type yyy and have called the function pqr off it. So, why is it
that an error is generated now, while it was not generated in the earlier
instance?
The answer lies in the manner in
which C# works internally. It divides all the classes it recognizes, into two
types, i.e. value types and reference types. Classes like int, bool and string,
which are called basic classes, are of value type, and all the other classes
are of reference type.
A value type variable is created
when we declare it. This means that i comes into existence and occupies memory
space when we declare it, using the syntax 'int i'. No extra code has to be
added after that, to create this.
In the case of a reference type
variable, C# is first informed about the data type of the variable that we want
to create, and also the variable name that we would use to reference it. This
happens when we use the statement yyy a. In the next round, the keyword 'new'
or a function has to be used to formerly bring the variable into existence, and
then it is to be assigned a memory location, to enable it to reference members
of the specified data type.
Note, that unless the second step
is executed, the members in the class remain inaccessible to the object.
b1.cs
public class zzz : System.Web.UI.Page
{
public void abc(System.Object o, System.EventArgs e)
{
yyy a;
a = new yyy();
a.pqr();
}
}
public class yyy
{
public void pqr(){
}
}
No errors are visible any longer.
It is because we have used the keyword 'new' to create an object 'a', which
looks like yyy. In technical jargon, 'a' is now an instance of class yyy. Thus,
the only way to create an object is, by using the keyword 'new', followed by
the name of the class. Thereafter, we have to use a pair of opening and closing
curly braces to encompass the actual code of the class.
The instance 'a' now stores the
return value of 'new', which is a memory location. Henceforth, it can be used
to refer to all the members in the class.
b1.cs
public class zzz : System.Web.UI.Page
{
public void abc(System.Object o, System.EventArgs e)
{
xxx a;
a = new xxx();
a.pqr();
}
}
public class yyy
{
public void pqr()
{
}
}
public class xxx : yyy
{
}
In this program, we have gone a
step further. The instance 'a' looks like xxx instead of yyy, and the function
pqr is called from class xxx. The class xxx is derived from class yyy,
following the simple rule of placing a colon first, followed by the name of the
class yyy. Thus, the class xxx inherits all members of class yyy. For the
moment, the class yyy has only one function called pqr, which the class xxx
'inherits'. Therefore, even though the class xxx does not contain the function
pqr, the object 'a' of type xxx can call it, which is because the class xxx
inherits the same from the class yyy.
We need to explore the C#
programming language in greater depth to be able to fully understand what
ASP.NET does with the C# code written in a .cs file. In order to steer clear of
clutter, we have created a sub directory called aaa in the root where we have
written the following program in a file called b1.cs.
c:\>md aaa
c:\aaa>edit b1.cs
b1.cs
public class zzz
{
}
This program is very similar to
the ones we have written earlier. The code written in a .cs file must be
converted into an executable program, as the web server and the web browser are
not involved here. To create an executable file, we use the command csc along
with the file name. In simple terms, the command csc can be called a program
which creates an executable, but in the context of the C# programming language,
it is identified as the C# compiler. Thus, the command is as follows: C:\aaa>csc b1.cs
Output
Microsoft (R) Visual C# Compiler Version 7.00.9254 [CLR
version v1.0.2914]
Copyright (C) Microsoft Corp 2000-2001. All rights reserved.
error CS5001: Program 'b1.exe' does not have an entry point
defined
The compiler reports an error,
since there is no 'entry point' defined in the exe file. What is an entry
point? Whenever a C# program begins execution, it starts execution from a base
function. This base function is called an entry point function.
Here, we are getting into the
details of the C# programming language, because all the code written in ASP+ is
first converted into C# code and then executed.
b1.cs
public class zzz
{
public static void Main()
{
System.Console.WriteLine("hi");
}
}
Output
hi
Akin to the pqr function in yyy,
we have introduced a function called Main within zzz in the file b1.cs. This
function calls another function named System.Console.WriteLine.
System.Console.WriteLine comprises of a parameter, which is a string called
'hi'. The compiler now successfully generates 'b1.exe' in the same
subdirectory, without displaying any error messages. If you run this program at
the command prompt, using the command
'c:\aaa>b1', the output displayed will be the word 'hi'. This proves
that Main is the base function or the entry point in any C# program.
You may have noticed that the
System.Console.WriteLine function in a.cs program, behaves like the
Response.Write function of ASP+.
b1.cs
public class zzz
{
public static void Main()
{
System.Console.WriteLine("hi");
abc();
}
public void abc()
{
System.Console.WriteLine("abc");
}
}
Output
b1.cs(6,1): error CS0120: An object reference is required for
the nonstatic field, method, or property 'zzz.abc()'
Despite having reiterated this
concept on numerous occasions, we shall go over it once again, that 'Only an
object i.e. an instance of a class, is allowed access to the members of that
class'. Thus, in the above program, we are not allowed to call the function
abc, since we have still not created an instance of the class zzz.
However, there is an exception to
this rule, which we shall introduce in the next program.
b1.cs
public class zzz
{
public static void Main()
{
System.Console.WriteLine("hi");
abc();
}
public static void abc()
{
System.Console.WriteLine("abc");
}
}
Output
hi
abc
We have added the keyword
'static' to the function abc. The keyword 'static' allows access to functions
or variable, even though, an instance of the class wherein it is contained, has
not been created. Hence, no error is generated. First the word 'hi' is
displayed on the screen, which is followed by the word 'abc'.
We shall employ one more program
to demonstrate the functionality of the keyword 'static'.
b1.cs
public class zzz
{
public static void Main()
{
System.Console.WriteLine("hi");
yyy.abc();
yyy a;
a = new yyy();
a.pqr();
}
}
public class yyy
{
public static void abc()
{
System.Console.WriteLine("abc");
}
public void pqr()
{
System.Console.WriteLine("pqr");
}
}
Output
hi
abc
pqr
In the class yyy, we have two
functions:
• The function abc, which has the
static keyword.
• The function pqr, which is
non-static.
To summon a static function, we
simply use the name of the class, followed by the function name i.e. yyy.abc.
Since the function abc is static, there is no need to create an object that is
an instance of the class yyy. In short, a static function is associated with a
class and not with an object.
In the case of the function pqr,
the rules get transformed completely. Since pqr is a non-static function, we
have to first create an object 'a' that is an instance of class yyy, and then
use the object to call the function pqr. The syntax is as follows: a.pqr().
b1.cs
public class zzz
{
public static void Main()
{
System.Console.WriteLine("hi");
yyy.abc();
yyy a;
a = new yyy();
a.pqr();
}
}
namespace aaa
{
public class yyy
{
public static void abc()
{
System.Console.WriteLine("abc");
}
public void pqr()
{
System.Console.WriteLine("pqr");
}
}
}
Output
Microsoft (R) Visual C# Compiler Version 7.00.9254 [CLR
version v1.0.2914]
Copyright (C) Microsoft Corp 2000-2001. All rights reserved.
b1.cs(6,1): error CS0246: The type or namespace name 'yyy'
could not be found (are you
missing a using directive or an assembly reference?)
b1.cs(7,1): error CS0246: The type or namespace name 'yyy'
could not be found (are you
missing a using directive or an assembly reference?)
b1.cs(8,1): error CS0103: The name 'a' does not exist in the
class or namespace 'zzz'
b1.cs(9,1): error CS0246: The type or namespace name 'a' could
not be found (are you missing a using directive or an assembly reference?)
On adding the words 'namespace
aaa' to the program, we obtain four errors. Why does this happen? The answer
reveals itself in the next program.
b1.cs
public class zzz
{
public static void Main()
{
System.Console.WriteLine("hi");
aaa.yyy.abc();
aaa.yyy a;
a = new aaa.yyy();
a.pqr();
}
}
namespace aaa
{
public class yyy
{
public static void abc()
{
System.Console.WriteLine("abc");
}
public void pqr()
{
System.Console.WriteLine("pqr");
}
}
}
Output
hi
abc
pqr
The errors have disappeared. This
could only be achieved by preceding the class name, i.e. yyy in Main, with the
namespace aaa, and then using the dot as the delimiter. Here, aaa is called a
namespace.
The concept of a namespace is
very simple.
Let us presume that in the .NET
world, there are a million functions. A programmer will go bonkers trying to
recollect the names of all these functions, along with the names of their
respective classes. It surely makes more sense to tidy them up by organizing them
in a systematic and logical fashion?
All printing functions can be
placed in one category, while all functions that write to a file can be placed
in another category. Instead of christening this type of classification as
'categories', the C# programming language makes use of the term 'namespace'. To
use a class belonging to a particular namespace, we have to use the format
'namespace.class'.
As our class yyy belongs to the
aaa namespace, the full name of the class becomes aaa.yyy, and not merely yyy.
b1.cs
public class zzz {
public static void Main(){
System.Console.WriteLine("hi");
aaa.bbb.yyy.abc();
aaa.bbb.yyy a;
a = new aaa.bbb.yyy();
a.pqr();
}
}
namespace aaa{
namespace bbb{
public class yyy
{
public static void abc()
{
System.Console.WriteLine("abc");
}
public void pqr()
{
System.Console.WriteLine("pqr");
}
}
}
}
Output
hi
abc
pqr
We can nest namespaces to as many
levels as desired. Here, we have a primary namespace called aaa containing a
secondary namespace called bbb. The full name of our class yyy therefore
becomes aaa.bbb.yyy, and the static function abc can be called using
aaa.bbb.yyy.abc.
Whenever we read an entity in C#,
we read it backwards, i.e. from right to left. We have the name of the function
at the right most end. This is preceded by the name of the class, and the class
name is preceded by the name of the namespace.
In the past, whenever we had
derived from System.Web.UI.Page, Page was the name of the class and
System.Web.UI was the name of the namespace.
Similarly, in the case of
Response.Write, Response is the name of a class, while Write is a static
function contained in it. In the past, we had deliberately tried to shield you
from these complicated and perplexing concepts. This was for the simple reason
that, we believe in divulging such intricate concepts only when the time is
right for them to be appreciated.
b1.cs
using aaa.bbb;
public class zzz
{
public static void Main()
{
System.Console.WriteLine("hi");
yyy.abc();
yyy a;
a = new yyy();
a.pqr();
}
}
namespace aaa
{
namespace bbb
{
public class yyy
{
public static void abc()
{
System.Console.WriteLine("abc");
}
public void pqr()
{
System.Console.WriteLine("pqr");
}
}
}
}
Output
hi
abc
pqr
It shall prove to be incredibly
cumbersome if we have to write aaa.bbb.yyy, every time we want to refer to the
class yyy. In order to overcome this difficulty, C# offers to us a short cut in
the form of a keyword called 'using'.
The keyword 'using' is followed
by the namespace and terminated by a semicolon. Every time a class name appears
in Main, C# replaces it with its long
name, by placing the namespace in front of it.
In case of any mismatch, the error message 'namespace.class not found'
is generated. The keyword 'using' is similar to shorthand. It gives you the
freedom to write the name of a class without actually mentioning the namespace.
At the same time, the keyword 'using' can occur as many times as you want it
to, in your program.
Constructors
b1.cs
public class zzz
{
public static void Main()
{
yyy a;
System.Console.WriteLine("hi");
a = new yyy();
}
}
public class yyy
{
public yyy()
{
System.Console.WriteLine("yyy");
}
}
Output
hi
yyy
Here, we have performed something
that is unusual and anomalous, that is to say, we have assigned the same name
to the function as to the class, i.e. yyy. Whenever a function has the same
name as that of the class, it is treated like a special function. It is called
a constructor. A constructor is automatically called, whenever an instance of
the class is created.
So, the code that is to be
executed when an object is created, is normally placed in the constructor. You
are free to place any code in the constructor.
b1.cs
public class zzz
{
public static void Main()
{
yyy a;
System.Console.WriteLine("hi");
a = new yyy();
}
}
public class yyy
{
public void yyy()
{
System.Console.WriteLine("yyy");
}
}
Output
b1.cs(12,13): error CS0542: 'yyy': member names cannot be the
same as their enclosing type
The syntax 'new yyy ()' creates
an instance of the class yyy. This implies that, it internally allocates memory
for the methods and the variables of the class yyy. The constructor is called,
as the last action in this sequence.
At the time when the constructor
is being executed, the object has not been created. Thus, constructors cannot
return any values, because there is no object available to accept these values.
The void data type signifies that there is no return value. However, in the
case of constructors, there is no possibility of any value being repeated. The
C# compiler assumes that yyy is a normal function because of the return type.
Since a normal function cannot possess the same name as the name of the class,
the above error message is generated.
An object is created only after
the constructor finishes executing its code successfully.
b1.cs
public class zzz
{
public static void Main()
{
yyy a;
System.Console.WriteLine("hi");
a = new yyy();
a.yyy();
}
}
public class yyy
{
public yyy()
{
System.Console.WriteLine("yyy");
}
}
Output
b1.cs(8,1): error CS0117: 'yyy' does not contain a definition
for 'yyy'
A constructor is a function with
only two restrictions:
• The first is that it cannot return
any values, as explained above.
• The second is that we cannot call a
constructor explicitly. It gets called
automatically, only during the creation of an instance of a class.
b1.cs
public class zzz
{
public static void Main()
{
yyy a;
a = new yyy();
a.abc();
a.abc("hi");
a.abc(10);
}
}
public class yyy
{
public void abc()
{
System.Console.WriteLine("abc");
}
public void abc(string s)
{
System.Console.WriteLine("abc " + s);
}
public void abc(int i)
{
System.Console.WriteLine("abc "+ i);
}
}
Output
abc
abc hi
abc 10
In C#, we are allowed to create
different functions having the same name, as long as they accept different
number and types of parameters. We may be acquainted with a function only by
its name, but C# identifies a function by using its name, as well as, through
the number of its parameters along with their data types.
We have created three separate
functions in our program using the same function i.e. abc. However, since the
parameter types are different in each case, C# flags them as separate
functions.
b1.cs
public class zzz
{
public static void Main()
{
yyy a;
a = new yyy("hi");
}
}
public class yyy
{
public yyy()
{
System.Console.WriteLine("yyy");
}
}
Output
b1.cs(6,5): error CS1501: No overload for method 'yyy' takes
'1' arguments
A constructor is basically a
function. So, all the rules of a function also apply to a constructor. Thus, in
the case of yyy, we need a function/constructor that accepts a string as a
parameter.
b1.cs
public class zzz
{
public static void Main()
{
yyy a;
a = new yyy("hi");
}
}
public class yyy
{
public yyy()
{
System.Console.WriteLine("yyy");
}
public yyy(string s)
{
System.Console.WriteLine("yyy " + s);
}
}
Output
yyy hi
To eliminate the above error, we
have introduced one more constructor that takes a single string type parameter.
The constructor that has more than one parameter is ignored, and the one with a
single parameter is called. Thus, when we create an object using 'new', only
the constructors that pre-exist in the class are called. If a constructor with
a specific number of parameters, does not exist, we cannot use 'new' to create
an object with those many number of parameters, because it will generate an
error.
b1.cs
public class zzz
{
public static void Main()
{
yyy a;
a = new yyy();
}
}
public class yyy
{
public yyy(string s)
{
System.Console.WriteLine("yyy " + s);
}
}
Output
b1.cs(6,5): error CS1501: No overload for method 'yyy' takes
'0' arguments
Life consists of unexpected
twists and turns, which adds, in no small measure, to its excitement. Earlier,
when we had not used constructors in our code, C# allowed us to use the keyword
'new' with abandon. However unexpectedly, when constructors were introduced, it
raised several objections.
The rationale behind this is,
when we have a class without any constructors, by default, the compiler
introduces a constructor that has no parameters, as in public yyy() { }. This
is provided to us for free. Thus, when we have no constructors in a class, we
are provided with one that has no parameters.
But, if we explicitly introduce
even a single constructor, the free constructor brought in by the compiler, is
retracted. In class yyy, since we have a constructor with one parameter, the
free constructor that has no parameters, is not added. Hence, the above error
is generated.
b1.cs
public class zzz
{
public static void Main()
{
yyy a;
a = new yyy();
}
}
public class yyy
{
public yyy()
{
System.Console.WriteLine("yyy ");
}
public yyy(string s)
{
System.Console.WriteLine("yyy " + s);
}
}
Output
yyy
The only way of getting rid of
the error message is by introducing a constructor that has no parameters in the
class yyy.
b1.cs
public class zzz {
public static void Main()
{
System.Console.WriteLine("main");
yyy a;
System.Console.WriteLine("main 1");
a = new yyy();
}
}
public class yyy
{
public yyy()
{
System.Console.WriteLine("yyy ");
}
static yyy()
{
System.Console.WriteLine("yyy static");
}
}
Output
main
main 1
yyy static
yyy
Static constructors are always
called before the non-static or instance constructors. Moreover, they get
called before any member of a class can be accessed.
b1.cs
public class zzz
{
public static void Main()
{
System.Console.WriteLine("main");
yyy.abc();
yyy a;
System.Console.WriteLine("main 1");
}
}
public class yyy
{
public yyy()
{
System.Console.WriteLine("yyy ");
}
static yyy()
{
System.Console.WriteLine("yyy static");
}
public static void abc()
{
System.Console.WriteLine("abc");
}
}
Output
main
yyy static
abc
main 1
The above program demonstrates
that the static constructor is called before the static function abc. As we are
not creating an object that is an instance of class yyy, the non-static
constructor of class yyy is never called.
Interfaces
b1.cs
public class zzz
{
public static void Main()
{
}
}
public interface yyy
{
}
An interface is similar to a
class. It is a reserved word in the C# programming language. Since it has been
used correctly from a syntactical point of view, no errors are generated.
b1.cs
public class zzz
{
public static void Main()
{
yyy a;
}
}
public interface yyy
{
}
We are also permitted to declare
an object that looks like an interface, yyy. No errors are generated at all. A
question that vexes us, is- 'Are interfaces and classes similar?' Do not lose sleep over this, since they are
as similar to each other, as a fish is to a fowl.
b1.cs
public class zzz
{
public static void Main()
{
yyy a;
a = new yyy();
}
}
public interface yyy
{
}
Output
b1.cs(6,5): error CS0144: Cannot create an instance of the
abstract class or interface 'yyy'
The first difference between an
interface and a class is that, an interface cannot be instantiated by using the
keyword 'new'. We can use 'new' only to instantiate a class and not an
interface.
b1.cs
public class zzz : yyy
{
public static void Main()
{
zzz a;
a = new zzz();
}
}
public interface yyy
{
void abc();
}
Output
b1.cs(1,14): error CS0535: 'zzz' does not implement interface
member 'yyy.abc()'
An interface can only consist of
function prototypes, i.e. the function name with its parameters. Hence, yyy is
an interface that contains only one function called abc, that takes no
parameters and returns a void. You may have noticed that we have not used the
open and close curly braces to enclose the function code. Instead, we have
terminated the function with a semicolon. Class zzz is derived from the
interface yyy in the usual manner, i.e. by using a colon sign. We get an error
message because the code of the function abc, is not present anywhere in the
class.
When one class derives from
another class, it inherits all the functions of the base class. But when a
class derives from an interface, i.e. when it implements an interface, the code
for the functions in the interface, has to be explicitly specified in the
class.
b1.cs
public class zzz : yyy
{
public static void Main()
{
zzz a;
a = new zzz();
a.abc();
}
public void abc()
{
System.Console.WriteLine("abc");
}
}
public interface yyy
{
void abc();
}
Output
abc
The error message has vanished,
since we have implemented all the functions in the interface yyy. If the
interface yyy has 10 functions or definitions or prototypes, we have to add the
code for all these functions in the class zzz.
b1.cs
public class zzz : yyy,xxx
{
public static void Main()
{
zzz a;
a = new zzz();
}
public void abc()
{
System.Console.WriteLine("abc");
}
public void pqr()
{
System.Console.WriteLine("pqr");
}
}
public interface yyy
{
void abc();
}
public interface xxx
{
void pqr();
}
We are allowed to derive from as
many interfaces as we like. This is not the case with classes.
In the above program, we are
deriving from two interfaces, viz. yyy and xxx at the same time. All that is
required to be done is, to ensure that we have added the code for functions abc
and pqr, in the class zzz.
b1.cs
public class zzz : yyy,xxx
{
public static void Main()
{
}
}
public class yyy
{
}
public class xxx
{
}
Output
b1.cs(1,24): error CS0527: 'xxx' : type in interface list is
not an interface
As we have stated earlier, we are
not allowed to derive from two classes. In C#, multiple inheritance from
classes is strictly prohibited.
a.aspx
<%@ language=C# %>
hi
<%
Respone.Write("hi");
error
%>
We have created a simple aspx
called a.aspx, and created an error in the C# code. The reason behind doing
this is that, we intend to discover the code that is generated by ASP+.
When we clicked on the last link
called + Show Complete Compilation Source, we got the following output:
Output
Line 1: //--------------------------------------------------------------
Line 2: //
<autogenerated>
Line 3: // This code was generated by a tool.
Line 4: // Runtime Version: 1.0.2914.16
Line 5: //
Line 6: // Changes to this file may cause incorrect
behavior and will be lost if
Line 7: // the code is regenerated.
Line 8: //
</autogenerated>
Line 9:
//--------------------------------------------------------------
Line 10:
Line 11: namespace
ASP {
Line 12: using
System;
Line 13: using
System.Collections;
Line 14: using
System.Collections.Specialized;
Line 15: using
System.Configuration;
Line 16: using
System.Text;
Line 17: using
System.Text.RegularExpressions;
Line 18: using
System.Web;
Line 19: using
System.Web.Caching;
Line 20: using
System.Web.SessionState;
Line 21: using
System.Web.Security;
Line 22: using
System.Web.UI;
Line 23: using
System.Web.UI.WebControls;
Line 24: using
System.Web.UI.HtmlControls;
Line 25:
Line 26:
Line 27: public
class a_aspx : System.Web.UI.Page,
System.Web.SessionState.IRequiresSessionState {
Line 28:
Line 29:
private static System.Web.UI.AutomaticHandlerMethodInfos __autoHandlers;
Line 30:
Line 31:
private static bool __intialized = false;
Line 32:
Line 33:
private static System.Collections.ArrayList __fileDependencies;
Line 34:
Line 35:
public a_aspx() {
Line 36: System.Collections.ArrayList
dependencies;
Line 37:
if ((ASP.a_aspx.__intialized == false)) {
Line 38:
dependencies = new System.Collections.ArrayList();
Line 39:
dependencies.Add("C:\\Inetpub\\wwwroot\\a.aspx");
Line 40:
ASP.a_aspx.__fileDependencies = dependencies;
Line 41:
ASP.a_aspx.__intialized = true;
Line 42:
}
Line 43: }
Line 44:
Line 45:
protected override System.Web.UI.AutomaticHandlerMethodInfos
AutoHandlers {
Line 46:
get {
Line 47:
return ASP.a_aspx.__autoHandlers;
Line 48:
}
Line 49:
set {
Line 50:
ASP.a_aspx.__autoHandlers = value;
Line 51:
}
Line 52: }
Line 53:
Line 54:
protected System.Web.HttpApplication ApplicationInstance {
Line 55:
get {
Line 56:
return ((System.Web.HttpApplication)(this.Context.ApplicationInstance));
Line 57:
}
Line 58: }
Line 59:
Line 60:
public override string TemplateSourceDirectory {
Line 61:
get {
Line 62:
return "/";
Line 63:
}
Line 64: }
Line 65:
Line 66:
public override void InstantiateIn(System.Web.UI.Control control) {
Line 67:
this.__BuildControlTree(control);
Line 68: }
Line 69:
Line 70:
private void __BuildControlTree(System.Web.UI.Control __ctrl) {
Line 71:
__ctrl.SetRenderMethodDelegate(new
System.Web.UI.RenderMethod(this.__Render__control1));
Line 72: }
Line 73:
Line 74: private
void __Render__control1(System.Web.UI.HtmlTextWriter __output,
System.Web.UI.Control parameterContainer) {
Line 75:
__output.Write("\r\nhi\r\n");
Line 76:
Line 77:
#line 3 "C:\Inetpub\wwwroot\a.aspx"
Line 78:
Line 79:
Respone.Write("hi");
Line 80: error
Line 81:
Line 82:
Line 83:
#line default
Line 84: }
Line 85:
Line 86:
protected override void FrameworkInitialize() {
Line 87:
this.FileDependencies = ASP.a_aspx.__fileDependencies;
Line 88: }
Line 89:
Line 90:
public override int GetTypeHashCode() {
Line 91:
return 2141278421;
Line 92: }
Line 93: }
Line 94: }
Line 95:
The output displayed above is the
C# code generated by ASP+ in respect of our aspx file. Let us try and decipher
this output with the limited knowledge of C# that we have acquired so far.
We begin with a series of 'using'
statements that are followed by the names of the namespaces. These 'using'
statements ensure that we do not have to write the namespace names anymore. The
C# file generated by ASP+ has a class name called a_aspx, since 'a' is the name
of our aspx file. This class is derived from System.Web.UI.Page, which means
that, all the code in the Page class is now available to a_aspx. The class also
implements an interface called IRequiresSessionState, which we shall touch
upon, a little later.
The class consists of a static
constructor a_aspx, which has no parameters. Amongst the other functions
present in the file, the most important function is __Render__control1. We will
ignore all the above code for the moment, and focus only on this function.
This function always gets called
by ASP+. All code other than the aspx code, i.e. the code in HTML or Javascript
etc., get added to this function. This function therefore, happens to be the
most crucial one. It accepts two parameters:
• The first parameter is output, which
is an instance of the class HtmlTextWriter in the System.Web.UI namespace.
Write is a function in the class HtmlTextWriter. Hence, it can be invoked by
calling output.Write.
• The second parameter to
__Render__control1 is parameterContainer. It is an instance of the class
Control in the System.Web.UI namespace. This parameter represents a control.
The HTML text 'hi' is converted into a parameter and passed to the function
Write .
The C# functions get copied word
for word, just as we had entered them. Hasn't ASP+ become more comprehensible
now? We have learnt a lot about ASP+ by making errors, and then analyzing the
code that was generated. We recommend the same for you.
Writing Components
One question that continues to
bother us is "How does C# compile the ASP generated code?" To understand this, we go back to our
subdirectory aaa and create two files called a.cs and b.cs.
a.cs
public class zzz
{
public static void Main()
{
yyy a;
a = new yyy();
a.abc();
}
}
Output
Microsoft (R) Visual C# Compiler Version 7.00.9254 [CLR
version v1.0.2914] Copyright (C) Microsoft Corp 2000-2001. All rights reserved.
a.cs(5,1): error CS0246: The type or namespace name 'yyy'
could not be found (are you missing a using directive or an assembly
reference?)
a.cs(6,1): error CS0103: The name 'a' does not exist in the
class or namespace 'zzz'
a.cs(7,1): error CS0246: The type or namespace name 'a' could
not be found (are you missing a using directive or an assembly reference?)
An error is generated, since C#
cannot locate the class yyy anywhere. So far, all our classes were stored in a
single file called a.cs. In this program, the class yyy is present in the file
b.cs.
b.cs
public class yyy
{
public void abc()
{
System.Console.WriteLine("abc");
}
}
Using the csc command, not only
can we create an executable file such as a.exe, but also a dll. The acronym dll
stands for Dynamic Link Library. Code that needs to be shared under Windows,
comes packaged in a dll. To create a dll, we use the same csc command, but with
different options. We use the following command
c:\aaa>csc /t:library /out:b.dll b.cs
The /t:library option instructs
the compiler to create a library or a dll. By default, the compiler creates an
exe file. Further, the /out: option allows us to assign a name to the file. Had
we not assigned any name, the default name would have been b.dll, since the
source file is named b.cs. Now that we have created our dll file, we simply
need to inform our csc compiler to look into this file, for the code of our
desired classes. Now, all that we do is, run csc as follows:
c:\aaa>csc /r:b.dll a.cs
The /r option asks the compiler
to look into b.dll for the code of those classes, that are not contained in the
file a.cs. The above program will produce a file called a.exe which, when
executed, will call the function abc from the class yyy. This concept is quite
simple and clear-cut, don't you agree ?
Let us now run the following aspx
file:
a.aspx
<%@ language=C# %>
<%
error
%>
We get an error message. When we
click on + Show Detailed Compiler
Output:, we see the following command:
C:\WINNT\system32>
"c:\winnt\microsoft.net\framework\v1.0.2914\csc.exe" /t:library
/utf8output /R:"c:\winnt\assembly\gac\system\1.0.2411.0__b77a5c561934e089\system.dll"
/R:"c:\winnt\assembly\gac\system.xml\1.0.2411.0__b77a5c561934e089\system.xml.dll"
/R:"c:\winnt\assembly\gac\system.drawing\1.0.2411.0__b03f5f7f11d50a3a\system.drawing.dll"
/R:"c:\winnt\assembly\gac\system.data\1.0.2411.0__b77a5c561934e089\system.data.dll"
/R:"c:\winnt\assembly\gac\system.web.services\1.0.2411.0__b03f5f7f11d50a3a\system.web.services.dll"
/R:"c:\winnt\microsoft.net\framework\v1.0.2914\mscorlib.dll"
/R:"c:\winnt\assembly\gac\system.web\1.0.2411.0__b03f5f7f11d50a3a\system.web.dll"
/out:"C:\WINNT\Microsoft.NET\Framework\v1.0.2914\Temporary ASP.NET
Files\root\fa7064c6\2014c0f1\jhbdrf-n.dll" /debug- /optimize+
"C:\WINNT\Microsoft.NET\Framework\v1.0.2914\Temporary ASP.NET
Files\root\fa7064c6\2014c0f1\jhbdrf-n.0.cs"
This appears quite complicated,
but it is very simple to follow.
ASP+, by default, runs in the
subdirectory of WINNT\system32. So, the C# compiler csc is invoked from this
directory. Now, since csc is not located in the system32 subdirectory, the
absolute path is given for the command i.e.
"c:\winnt\microsoft.net\framework\v1.0.2914\csc.exe"
You will also notice a large
number of /R options, that direct the compiler to look into all the specified
dlls for code of classes. In case we require code from a dll that is not listed
above, then we are surely out of luck.
Thereafter, the C# compiler is
instructed to create a library and not an exe file. This output file is given a
dll name by using the /out: option, and
it is stored in a particular directory
/out:"C:\WINNT\Microsoft.NET\Framework\v1.0.2914\Temporary
ASP.NET Files\root\fa7064c6\2014c0f1\ehcuumxx.dll"
The name assigned to the C#
program is ehcuumxx.0.cs. We can physically verify these files by going to the
specified subdirectories.
This goes on to prove that all
the HTML text and the C# code that we write in an aspx file, are finally packed
into a dll. When the aspx file called 'a', is loaded for the very first time,
it consumes a considerable amount of time. This is because, ASP+ has to first
write out the C# program, and then compile it to generate a dll. Finally, the
code is executed, which results in the display of the output in the browser
window. All this happens only once, and thus, it takes a very long time. But
subsequently, when the same file is requested off the server, the HTML file is
generated in a much speedier manner, since the dll has already been created.
a.aspx
<html>
<script language="C#" runat="server">
void Page_Load(Object s, EventArgs E)
{
int i = 10;
short j = 2;
j = i;
}
</script>
</body>
</html>
Output
Compiler Error Message: CS0029: Cannot implicitly convert type
'int' to 'short'
Line 7: j = i;
We have created two variables of
two different data types, namely, i as an int and j as a short. We are not
allowed to equate two different data types with each other. Thus, we get an
error 'cannot covert to … line 7 j=i '. C# does not support the concept of
equating different data types.
a.aspx
<html>
<script language="C#" runat="server">
void Page_Load(Object s, EventArgs E)
{
int i = 10;
short j = 2;
j = (short)i;
}
</script>
</body>
</html>
The same program now shows no
error, which is because we made sure that both sides of the 'equal to' sign are
of the same data type. This is incorporated by means of using the round
brackets, and then by mentioning the data type to which we want the other side
converted. This does not change the value of i, since it is only for a short
time, that we change its data type, from an int to a short. Now, since both
sides of the 'equal to' sign have a short data type, we do no get any error.
The above pair of () brackets, with the data type mentioned within it, is
called a cast operator.
The next program employs user
controls, as by now, we are fairly familiar with the concept of 'new'.
a.aspx
<%@ Register TagPrefix="vijay"
TagName="mukhi" Src="a1.ascx" %>
<html>
<script language="C#" runat="server">
void Page_Load(Object s, EventArgs E)
{
Page.Controls.Add(new HtmlGenericControl("hr"));
UserControl c1 = (UserControl) LoadControl("a1.ascx");
((a1_ascx)c1).Category = "business";
Page.Controls.Add(c1);
}
</script>
</body>
</html>
a1.ascx
<script language="C#" runat="server">
public String aa;
public String Category
{
get
{
return aa;
}
set
{
aa = value;
}
}
</script>
<span >Sonal: <%=aa%></span>
Output
Sonal: business
Let us look at a.aspx. In the
Page_Load function, we first create an HTML tag 'hr', by using 'new'. This
control is then added to the list of controls, using the Add function in
Page.Controls. In this manner, we can dynamically add any control or tag we like,
to a page. The a1.ascx file that contains the control, is then loaded, using
the LoadControl function. The result of this function is a control that is
stored in an object called c1.
The control has a property called
Category, which is initialized to business. We need to cast it to a1_ascx,
since we need to convert c1, which looks like class UserControl, into an
a1_ascx class.
Due to the Register attribute on the control file named a1.ascx, ASP+ creates a class a1_ascx that represents our control. Hence, we need to use a cast operator, because the object c1 does not contain any property called Category. We use the Add function again to add c1 to the page.