Вы находитесь на странице: 1из 29

10

Properties and Indexers


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.

Вам также может понравиться