10
Properties
Properties are a natural extension to fields. Very few
programming languages support the notion of a property. Unlike a variable, a
property is not stored in a memory location. It is made up of functions. Thus
even though a property and a field share the same syntax a property has the
advantage that code gets called. When we initialize a variable, no code in our
class gets called. We are not able to execute any code for a variable access or
initialization at all. In the case of a property, we can execute tons of code.
This is one singular reason for the popularity of a product like Visual Basic -
the use of properties. One simple example is setting the value of a variable.
If it is through a variable, we have no control over the value used. If the
same access is through a property, the programmer has no inkling of whether it
is a property or a variable, we can build range checks to make sure that the
variable does not cross certain bounds.
Lets start by creating a simple property. A property is a
member of a class. It behaves like a variable for the user.
a.cs
public class zzz
{
public static void Main()
{
}
}
public class aa
{
public int ff {
}
}
Compiler Error
a.cs(9,12): error CS0548: ‘aa.ff’ : property or indexer must
have at least one accessor
We have tried to create a property called ff which is of
type int. We get an error because a property is used either on the left or the
right of an equal to sign. If we had created a variable ff, we would like to
write a statement as gg = ff + 9. Here
ff should return some value which is of the data type int.
a.cs
public class zzz
{
public static void Main()
{
aa a = new aa();
int gg = a.ff + 9;
System.Console.WriteLine(gg);
}
}
public class aa
{
public int
ff {
get
{
System.Console.WriteLine(“in
get”);
return 12;
}
}
}
Output
in get
21
A property should have at least one accessor, in our case, a
get as we want to read the value of the property. Thus a.ff calls the get
accessor which returns an int, in this case 12. If we did not have access to
the code of the class aa, we would have assumed ff to have been a variable.
a.cs
public class zzz
{
public static void Main()
{
aa a = new aa();
a.ff = 19;
System.Console.WriteLine(a.ff);
}
}
public class aa
{
public int ff {
get
{
System.Console.WriteLine(“in
get”);
return 12;
}
set
{
System.Console.WriteLine(value);
}
}
}
Output
19
in get
12
A variable can also be used on the left-hand side of the
equalto sign. In this case we are writing or changing the value of the
variable. We are passing it some value. If it is a property, ff in our case,
a.ff = 19 will call the accessor set. The set accessor has a free variable available in it called
value. It gets created automatically, we do not create this variable. In our
case, this has the value 19, which we are displaying in WriteLine. Then to
display the value of the property ff, the get needs to be called again. The get
always returns the same answer as the set does not store the value of the
variable anywhere. To resolve this issue, we do the following.
a.cs
public class zzz
{
public static void Main()
{
aa a = new aa();
a.ff = 19;
System.Console.WriteLine(a.ff);
}
}
public class aa
{
int f1;
public int ff {
get
{
System.Console.WriteLine(“in
get”);
return f1;
}
set
{
System.Console.WriteLine(“in
set “ + value);
f1 = value;
}
}
}
Output
in set 19
in get
19
To implement a property in real life, we create a public
variable which will hold the value of the property. This variable f1 will have
the same data type as the property i.e. an int in our case. In the get, we
return f1 and in the set we initialize f1 to value. This is the simplest case
possible.
The reason we use a property and not a variable is because
if we change the value of a variable/field,
then code in our class is not aware of the change. Also we have no control
over what values the variable will contain. The user can change them to
whatever he/she likes and we cannot implement range checks on the variable.
Also the user may want to associate some action with the changes in the value
of the variable. Using a property, reading or writing to the variable also can
be monitored.
a.cs
public class zzz
{
public static void Main()
{
aa a = new aa();
a.ff = 19;
System.Console.WriteLine(a.ff);
}
}
public class aa
{
int f1;
public int ff {
get
{
System.Console.WriteLine(“in
get”);
return f1;
}
}
}
Compiler Error
a.cs(6,1): error CS0200: Property or indexer ‘aa.ff’ cannot
be assigned to — it is read only
You are allowed to declare a property readonly by omitting
the set accessor. No one is now allowed to change the value of the property. It
now behaves as a const or readonly field.
a.cs
public class zzz
{
public static void Main()
{
aa a = new aa();
a.ff = 19;
}
}
public class aa
{
int f1;
public int ff {
set
{
System.Console.WriteLine(“in
set “ + value);
f1 = value;
}
}
}
Output
in set 19
Theoretically, you can have a property which is write only
i.e. only with a set accessor. With
set, you can change the value of ff but it is of limited use because you can
never access the value of ff. A property differs from a field by ending with
{}.
a.cs
public class zzz
{
public static void Main()
{
}
}
public class aa
{
public int ff {
set
{
}
}
public int ff {
get
{
}
}
}
Compiler Error
a.cs(12,12): error CS0102: The class ‘aa’ already contains a
definition for ‘ff’
You cannot create a property in 2 separate bits and pieces.
It has to be in one whole. This is part of the syntax. The above creates two
properties, both called ff, the first one being write only, the second, read only.
The compiler tells you that you cannot create two properties by the same name.
a.cs
public class zzz
{
public static void Main()
{
}
}
public class aa
{
private int ff;
public int ff {
get {
}
set {
}
}
}
Compiler Error
a.cs(10,12): error CS0102: The class ‘aa’ already contains a
definition for ‘ff’
You obviously cannot have a property and variable with the
same name. The compiler would not know whether to invoke the property or the
field. They both are stored in the same namespace.
a.cs
class zzz
{
public static void Main()
{
yyy.i = 20;
System.Console.WriteLine(yyy.i);
}
}
class yyy
{
public static int i
{
get {
System.Console.WriteLine(“get”);
return 10;
}
set {
System.Console.WriteLine(“set “ + value);
}
}
}
Output
set 20
get
10
The rules of static apply to properties also. Like variable
we access them using the class and not the instance. Everything that we have
learned about static in the past applies to properties also.
a.cs
class zzz
{
public static void Main()
{
yyy a = new yyy();
a.i = 100;
System.Console.WriteLine(a.i);
}
}
abstract class xxx
{
public abstract int i
{
get ;
set ;
}
}
class yyy : xxx
{
public override int i {
get
{
System.Console.WriteLine(“get”);
return 10;
}
set
{
System.Console.WriteLine(“set “ + value);
}
}
}
Output
set 100
get
10
The abstract property i in class xxx carries no code at all.
The get and set accessors are simply represented by a semicolon. In the derived
class, we must implement both the get and the set accessors. If we do not use
the override keyword, it is new. We hope you have finally understood new and
override.
a.cs
class zzz
{
public static void Main()
{
}
}
abstract class xxx
{
public abstract int i
{
get ;
}
}
class yyy : xxx
{
public override int i {
get
{
}
set
{
}
}
}
Compiler Error
a.cs(20,1): error CS0546: ‘yyy.i.set’: cannot override
because ‘xxx.i’ does not have an overridable set accessor
In class xxx, the abstract property has only a get accessor.
In the derived class we are implementing both the get and the set. The original
never ever had a set. This is unacceptable to the compiler. Thus we have no
choice but to implement only the accessors that are present in the original. A
get accessor can be viewed as a method which returns a value but accepts no
parameters.
a.cs
class yyy
{
public void i {
}
}
Compiler Error
a.cs(3,13): error CS0547: ‘i’ : property or indexer cannot
have void type
It makes no sense for an accessor to have a void type as a
variable cannot be of type void. Void literally means ‘I do not know the type’
or no type at all.
a.cs
class yyy
{
public int i {
set
{
return 10;
}
}
}
Compiler Error
a.cs(6,2): error CS0127: Since ‘yyy.i.set’ returns void, a
return keyword must not be followed by an object expression
A set accessor can be viewed as function which returns void
but accepts one parameter which stands for the value of the property. Thus a
set cannot return a value. If we remove the 10, we will not get an error.
a.cs
class zzz
{
public static void Main()
{
}
public int i {
set
{
value = 20;
}
}
}
The reserved variable value in the set can be changed at
will. Though, understanding why anyone would want to do such dumb stuff is
beyond us.
a.cs
class zzz
{
public static void Main()
{
}
public int i {
set
{
int value;
}
}
}
Compiler Error
a.cs(9,6): error CS0136: A local variable named ‘value’
cannot be declared in this scope because it would give a different meaning to
‘value’, which is already used in a ‘parent or current’ scope to denote
something else
We cannot however create a variable value as it will clash
with the variable value which is already present by default in the set.
a.cs
class zzz
{
public static void Main()
{
yyy a = new yyy();
a.i = 10;
xxx b = new xxx();
((yyy)b).i = 20;
b.i = 10;
}
}
class yyy
{
public int i {
set {
}
}
}
class xxx : yyy
{
public int i {
get {
return 10;
}
} }
Compiler Error
a.cs(9,1): error CS0200: Property or indexer ‘xxx.i’ cannot
be assigned to — it is read only
In the class yyy, the property i has only the set accessor.
In the class xxx which derives from yyy, we have implemented only the get
accessor. The property i in class xxx hides the i of yyy. They do not add up.
What we are trying to say is that both these properties are independent of each
other. What we had thought C# would have done is, taken the set from one class
and added it to the second. However, that does not make sense. It treats them
independently. If we want to use the property of the class yyy, then we need to
explicitly cast it as we have done for b. Thus the property i of class yyy gets
hidden but can be accessed.
A property is not necessarily slower than a variable. A
variable access normally initializes some memory, whereas a property executes a
method. This is not necessarily slower as at times, C# will rewrite your
property methods to memory accesses. This is called inlining of code. Except
for minor differences, all that we mentioned about virtual, abstract and new
apply also to a property. The difference is, if the original property has a get
and a set, the derived class will only implement a set or a get.
Indexers
An indexer lets us access members of a class as if it were
an array.
a.cs
public class zzz
{
public static void Main()
{
yyy a = new yyy();
a[1] = 10;
}
}
public class yyy {
}
Compiler Error
a.cs(6,1): error CS0021: Cannot apply indexing with [] to an
expression of type ‘yyy’
We have created an object a that looks like yyy. The object
a, in no sense of the word is an array. We are assuming that a is an array and
we’ve used the array syntax a[], hence it gives us an error.
a.cs
public class zzz
{
public static void Main()
{
yyy a = new yyy();
a[1] = 10;
}
}
public class yyy
{
public int this[int i]
{
set {
System.Console.WriteLine(“in get “ + value + “ “ + i);
}
}
}
Output
in get 10 1
We’ve added a few lines to have the array notation work with
an object that looks like yyy. To implement indexers, we need to create a
special property called this. This is a reserved word. As of now, we have a
parameter i (an int) in the square brackets. When we did properties earlier,we
learnt that a set gets called whenever we want to initialize or set a variable. Within the set accessor we have a
special variable called value which stores the value passed to the set, in this
case 10. The variable i will hold the value 1 as the array parameter is 1.
This is how we implement arrays when there are none.
a.cs
public class zzz
{
public static void Main()
{
yyy a = new yyy();
System.Console.WriteLine(a[1]);
}
}
public class yyy
{
public int this[int i]
{
set
{
System.Console.WriteLine(“in get “ + value + “ “ + i);
}
get
{
System.Console.WriteLine(“in set “ + i);
return 23;
}
}
}
Output
in set 1
23
The rules binding properties are applicabe to indexers too.
When you want to read the value of a[1], the get gets called. The major
difference between properties and indexers is that when you implement the code
for indexers you have to understand that the get and set get called with a
variable which is the array parameter value. The code will have to understand
array simulation.
a.cs
public class zzz
{
public static void Main()
{
yyy a = new yyy();
a[“hi”] = 30;
System.Console.WriteLine(a[“hi”]);
}
}
public class yyy
{
public int z;
public int this[string i]
{
set
{
System.Console.WriteLine(“in get “ + value + “ “ + i);
z = value;
}
get
{
System.Console.WriteLine(“in set “ + i);
return z;
}
}
}
Output
In get 30 hi
In set hi
30
The this property has a return value, in this case, an int.
Also the [] brackets can contain data types other than an int. In this case a
string. The string i has a value hi as
that is what we passed in the array brackets. You can have two this’s in your
class. You have to decide what data type to use in the array brackets. An
indexer is very useful when you have a database object and you want to access
the data in the fields using a notation [“fieldname”]
Indexers follow the same concepts of virtual, new, override
etc.
a.cs
class zzz
{
public static void Main()
{
yyy a = new yyy();
a[1] = 10;
a[“one”] = 10;
a[“hi”,2] = 30;
}
}
class yyy
{
public int this [ int i]
{
set
{
System.Console.WriteLine(“one int “+ i + “ “ + value);
}
}
public int this [ string i]
{
set
{
System.Console.WriteLine(“one string “+ i + “ “ + value);
}
}
public int this [ string i, int j]
{
set
{
System.Console.WriteLine(“one string and int “+ i + “ “ + j +
“ “ + value);
}
}
}
Output
one int 1 10
one string one 10
one string and int hi 2 30
The signature of an indexer is the number and types of
formal parameters. The return value and the names of the parameters do not
contribute to the indexers signature. Thus we have overloaded the indexers to
take an int, string or a string int combination. Each time a different function
gets called. The point to understand is that all the indexers have to return
the same data type, in our case int. The same rules that apply to function
overloading apply here also. Functions cannot differ only by return values. We
are sure that for indexers in the next version, C# should/must make an
exception.
A property is identified by its name, an indexer by its
signature. There is no concept of property overloading in C#.
a.cs
class zzz
{
public static void Main()
{
}
}
class yyy
{
public static int this [ int i]
{
set
{
}
}
}
Compiler Error
a.cs(9,19): error CS0106: The modifier ‘static’ is not valid
for this item
A property can be both an instance member which is the
default or static. An indexer unfortunately can only be an instance member and
not static. God alone knows why this discrimination against indexers. Once
again no rational reason for the above error. Obviously you cannot create a
variable with the same name as that of the parameter passed in the indexer.
a.cs
class zzz
{
public static void Main()
{
xxx a = new xxx();
a[2] = 20;
System.Console.WriteLine(a[2]);
}
}
class yyy
{
public virtual int this [ int i]
{
get
{
System.Console.WriteLine(“yyy get “ + i);
return 20;
}
set
{
System.Console.WriteLine(“yyy set “ + value + “ “ + i);
}
}
}
class xxx : yyy
{
public override int this [ int i]
{
get
{
int p = base[i];
System.Console.WriteLine(“xxx get “ + i + “ “ + p);
return 200;
}
set
{
System.Console.WriteLine(“xxx set “ + value + “ “ + i);
base[i] = value;
}
}
}
Output
xxx set 20 2
yyy set 20 2
yyy get 2
xxx get 2 20
200
The above example deals with calling the indexers of the
base class. At times when we are overriding code in the derived class, we would
like to call the original indexer in the base class first. The first rule that
we have to adhere to is that the indexer in the base class must be declared
virtual. In the derived class, we are now declaring it with the modifier
override. Same rules as above. In the set accessor, we have to call the
original as base[i], where i is the index to the indexer. Also we need to pass
it the value to initialize itself. This is stored in the variable value. This
a[2] in Main gets replaced by base[2] in the set. In get the reverse takes
place. Here we need to place base[i] on the right of the equalto sign, the
original get will return a value, in this case 20, which we are storing in a
variable p. What we do with p as well as the value from the get is our
business.
a.cs
class yyy
{
public int this [ byte i , string j]
{
get
{
return 10;
}
set
{
}
}
int get_Item(byte i,string j)
{
return 20
}
void set_Item(byte i,string j , int value)
{
}
}
Compiler Error
a.cs(5,1): error CS0111: Class ‘yyy’ already defines a
member called ‘get_Item’ with the same parameter types
a.cs(9,1): error CS0111: Class ‘yyy’ already defines a
member called ‘set_Item’ with the same parameter types
Like a property, an indexer also gets a name change. If people can get their bodies pierced then why cannot a indexer get converted to a series of functions starting with get? For a get, the parameters are the same as we pass to an indexer. It has a return value and the type of the indexer. Also the set has one more added parameter and that is the free variable value.