- •Introduction
- •TYPE & RUN 1
- •TYPE & RUN 2
- •9 Handling Problems in Your Programs: Exceptions and Errors
- •10 Reusing Existing Code with Inheritance
- •11 Formatting and Retrieving Information
- •12 Tapping into OOP: Interfaces
- •13 Making Your Programs React with Delegates, Events, and Indexers
- •14 Making Operators Do Your Bidding: Overloading
- •16 Creating Windows Forms
- •17 Creating Windows Applications
- •18 Working with Databases: ADO.NET
- •20 Creating Web Applications
- •21 A Day for Reflection and Attributes
- •Appendices
- •Index
Tapping into OOP: Interfaces |
429 |
LISTING 12.2 continued
25: } 26:
27:public int Sides
28:{
29:get { return InSides; }
30:set { InSides = value; }
31:}
32:
33:public Square()
34:{
35:Sides = 4;
36:}
37:}
38:
39:public class Props
40:{
41:public static void Main()
42:{
43:Square mySquare = new Square();
44:mySquare.SideLength = 5;
45:
46:Console.WriteLine(“\nDisplaying Square information:”);
47:Console.WriteLine(“Area: {0}”, mySquare.Area());
48:Console.WriteLine(“Sides: {0}”, mySquare.Sides);
49:}
50:}
Displaying Square information: |
12 |
OUTPUT Area: 25 |
Sides: 4
This listing focuses on the use of a property rather than all the other code in the previous listing. You can see that the number of sides for the shape is now
accessed via a property instead of a method. In Lines 8–12, the IShape interface has a declaration for a property named Sides that will be used with an integer. This will have both the get and set methods. You should note that you are not required to specify both here; it would be perfectly acceptable to specify just a get or just a set. If both are specified in the interface, all classes that implement the interface must implement both.
Note
In the IShape interface used here, it would make sense to specify only the get property for Sides. Many shapes have a specific number of sides that could be set in the constructor and then never changed. The get method could still be used. If set were not included in the interface, a class could still implement set.
430 |
Day 12 |
The IShape interface is implemented in a Square class starting in Line 17. In Lines 27–31, the actual definitions for the get and set properties are defined. The code for the Square class’s implementation is straightforward. The Sides property sets the InSides data member.
Using a property that has been implemented via an interface is no different than using any other property. You can see the use of the Sides property in the previous listing in a number of lines. This includes getting the value in Line 48. The value is set in line 35 of the constructor.
Note
Many people will say that a class inherits from an interface. In a way, this is true; however, it is more correct to say that a class implements an interface.
Using Multiple Interfaces
One of the benefits of implementing interfaces instead of inheriting from a class is that you can implement more than one interface at a time. This gives you the power to do multiple inheritance without some of the downside.
To implement multiple interfaces, you separate each interface with a comma. To include both an IShape and an IShapeDisplay interface in a Square class, you use the following:
class Square : IShape, IShapeDisplay
{
...
}
You then need to implement all the constructs within both interfaces. Listing 12.3 illustrates the use of multiple interfaces.
LISTING 12.3 Multi.cs—Implementing Multiple Interfaces in a Single Class
1:// Multi.cs -
2://--------------------------------------------------------------------
4: using System; 5:
6:public interface IShape
7:{
8:// Cut out other methods to simplify example.
9:double Area();
10:int Sides { get; }
11:}
Tapping into OOP: Interfaces
LISTING 12.3 continued
12:
13:public interface IShapeDisplay
14:{
15:void Display();
16:}
17:
18:public class Square : IShape, IShapeDisplay
19:{
20:private int InSides;
21:public int SideLength;
22:
23:public int Sides
24:{
25:get { return InSides; }
26:}
27:
28:public double Area()
29:{
30:return ((double) (SideLength * SideLength));
31:}
32:
33:public double Circumference()
34:{
35:return ((double) (Sides * SideLength));
36:}
37:
38:public Square()
39:{
40:InSides = 4;
41:}
42:
43:public void Display()
44:{
45:Console.WriteLine(“\nDisplaying Square information:”);
46:Console.WriteLine(“Side length: {0}”, this.SideLength);
47:Console.WriteLine(“Sides: {0}”, this.Sides);
48:Console.WriteLine(“Area: {0}”, this.Area());
49:}
50:}
51:
52:public class Multi
53:{
54:public static void Main()
55:{
56:Square mySquare = new Square();
57:mySquare.SideLength = 7;
58:
59:mySquare.Display();
60:}
61:}
431
12
432 |
Day 12 |
Displaying Square information:
Side length: 7
Sides: 4
Area: 49
You can see that two interfaces are declared and used in this listing. In Line 18, you can see that the Square class will implement the two interfaces. Because both
are included, all members of both interfaces must be implemented by the Square class. In looking at the code in Lines 23–49, you see that all the members are implemented.
Using Explicit Interface Members
So far, everything has gone smoothly with implementing interfaces. What happens, however, when you implement an interface that has a member name that clashes with another name already in use? For example, what would happen if the two interfaces in List-
ing 12.3 both had a Display method?
If a class includes two or more interfaces with the same member name, that member needs to be implemented only once. This single implementation of the method satisfies both interfaces.
Sometimes you want to implement the method independently for both interfaces. In this case, you need to use explicit interface implementations. An explicit implementation is done by including the interface name with the member name when you define the member. You must also use casting to call the method, as shown in Listing 12.4.
LISTING 12.4 Explicit.cs
1:// Explicit.cs -
2://--------------------------------------------------------------------
4: using System; 5:
6:public interface IShape
7:{
8:double Area();
9:int Sides { get; }
10:void Display();
11:}
12:
13:public interface IShapeDisplay
14:{
15:void Display();
16:}
17:
Tapping into OOP: Interfaces
LISTING 12.4 continued
18:public class Square : IShape, IShapeDisplay
19:{
20:private int InSides;
21:public int SideLength;
22:
23:public int Sides
24:{
25:get { return InSides; }
26:}
27:
28:public double Area()
29:{
30:return ((double) (SideLength * SideLength));
31:}
32:
33:public double Circumference()
34:{
35:return ((double) (Sides * SideLength));
36:}
37:
38:public Square()
39:{
40:InSides = 4;
41:}
42:
43:void IShape.Display()
44:{
45:Console.WriteLine(“\nDisplaying Square Shape\’s information:”);
46:Console.WriteLine(“Side length: {0}”, this.SideLength);
47:Console.WriteLine(“Sides: {0}”, this.Sides);
48:Console.WriteLine(“Area: {0}”, this.Area());
49:}
50:void IShapeDisplay.Display()
51:{
52:Console.WriteLine(“\nThis method could draw the shape...”);
53:}
54: 55: } 56:
57:public class Explicit
58:{
59:public static void Main()
60:{
61:Square mySquare = new Square();
62:mySquare.SideLength = 7;
63:
64:IShape ish = (IShape) mySquare;
65:IShapeDisplay ishd = (IShapeDisplay) mySquare;
433
12
434 |
Day 12 |
LISTING 12.4 continued
67:ish.Display();
68:ishd.Display();
69:}
70:}
Displaying Square Shape’s information:
Side length: 7
Sides: 4
Area: 49
This method could draw the shape...
This listing is a bit more complicated, but the result is that you can explicitly declare and then use methods from different interfaces with the same name
within a single class.
This listing has two methods named Display. Each of these is explicitly defined within the Square class. You can see in Lines 43 and 50 that the explicit definitions use the explicit name of the method. The explicit name is the interface name and the member name separated by a period.
Using these explicit interfaces requires more work than calling the method. After all, if you call the method using the standard class name, which Display method would be used? To use one of the methods, you must cast the class to the interface type. In this case, it is a matter of casting the class to either IShape or IShapeDisplay. In Line 64, a variable, ish, is declared that is of type IShape. This is assigned to the mySquare class. A cast makes sure that the mySquare class is treated as an IShape type.
In Line 65, the myShape class is cast to a variable, ishd, that is of type IShapeDisplay. You can see in Lines 67–68 that these variables of interface types can then be used to call the appropriate Display method.
The end result of this listing is that you can have multiple interfaces with similarly named methods. Using explicit definitions and a little casting, you can make sure that the correct method is called. Why might you do this? For the previous listing, the IShapeDisplay interface might be used with shapes to ensure that all the classes have the capability of doing a graphical display method. The Display method in the IShape
might have the purpose of providing detailed textual information. By implementing both, you have the capability to get both types of display.
Tapping into OOP: Interfaces |
435 |
Deriving New Interfaces from Existing Ones
As with classes, an interface can be derived from another interface. This inheritance of interfaces is done in a similar manner to inheriting classes. The following snippet shows how the IShape interface created earlier could be extended:
public interface IShape
{
long Area();
long Circumference(); int Sides{ get; set; };
}
interface I3DShape : IShape
{
int Depth { get; set; }
}
The I3DShape contains all the members of the IShape class and any new members that it adds. In this case, a Depth property member is added. You can then use the I3DShape interface as you would any other interface. Its members would be Area, Circumference, Sides,
and Depth.
Hiding Interface Members
It is possible to implement an interface member and yet hide its access from the base |
|
|
class. This can be done to meet the requirement of implementing the interface and to |
|
|
|
||
avoid cluttering up your class with additional members. |
12 |
|
To hide an interface, you explicitly define it in the class. Listing 12.5 provides an exam- |
||
|
||
ple of hiding an interface member. |
|
|
|
|
LISTING 12.5 Hide.cs—Hiding an Interface Member from a Class
1:// Hide.cs -
2://--------------------------------------------------------------------
4: using System; 5:
6:public interface IShape
7:{
8:// members left out to simplify example...
9:int ShapeShifter( int val );
10:int Sides { get; set; }
11:}
12:
436 |
Day 12 |
LISTING 12.5 continued
13:public class Shape : IShape
14:{
15:private int InSides;
16:
17:public int Sides
18:{
19:get { return InSides; }
20:set { InSides = value; }
21:}
22:
23:int IShape.ShapeShifter( int val )
24:{
25:Console.WriteLine(“Shifting Shape....”);
26:val += 1;
27:return val;
28:}
29:
30:public Shape()
31:{
32:Sides = 5;
33:}
34:}
35:
36:public class Hide
37:{
38:public static void Main()
39:{
40:Shape myShape = new Shape();
42:Console.WriteLine(“My shape has been created.”);
43:Console.WriteLine(“Using get accessor. Sides = {0}”, myShape.Sides);
45: |
// |
myShape.Sides = myShape.ShapeShifter(myShape.Sides); |
// error |
46: |
|
|
|
47:IShape tmp = (IShape) myShape;
48:myShape.Sides = tmp.ShapeShifter( myShape.Sides);
50:Console.WriteLine(“ShapeShifter called. Sides = {0}”, myShape.Sides);
51:}
52:}
My shape has been created.
OUTPUT Using get accessor. Sides = 5
Shifting Shape....
ShapeShifter called. Sides = 6
Tapping into OOP: Interfaces |
437 |
ANALYSIS |
This listing uses a scaled-down version of the IShape interface that you’ve seen |
|
|
used throughout today’s lessons. The focus of this listing is to illustrate the point |
|
|
|
|
of hiding an interface’s member from a class. In this case, the ShapeShifter method is |
|
|
hidden from the Shape class. Line 45, which is commented out, is an attempt to use the |
|
|
ShapeShifter method as a member of the Shape class. If you remove the comments from |
|
|
the beginning of this line and try to compile and run this program, you get the following |
|
|
error: |
|
|
Hide2.cs(45,23): error CS0117: ‘Shape’ does not contain a definition for |
|
|
|
‘ShapeShifter’ |
|
As you can see, Shape objects can’t directly access the ShapeShifter method—it is hidden |
|
|
from them. |
|
|
How is this done? When defining the interface member, you need to do it explicitly. In |
|
|
Line 23, you see that the definition of the ShapeShifter method includes such an explicit |
|
|
definition. The name of the interface is explicitly included in this line. |
|
|
When calling the explicitly defined member, you need to do what was done in |
|
|
Lines 47–48: You need to declare a variable of the interface type and then use a cast of |
|
|
the interface type using the object that you want to access. In Line 47, you see that a |
|
|
variable named tmp is created that is of the interface type IShape. The myShape object is |
|
|
then cast to this variable using the same interface type. In Line 48, you see that this vari- |
|
|
able of the interface type (IShape) can be used to get to the ShapeShifter method. The |
|
|
output from Line 50 proves that the method was appropriately called. |
|
|
The tmp variable can access the method because it is of the same type as the explicit dec- |
12 |
|
laration in Line 23. |
Summary
In today’s lesson you learned about interfaces, a construct that enables you to define what must be implemented. Interfaces can be used to ensure that different classes have similar implementations within them. You learned a number of things about interfaces, including how to use them, how to extend them, and how to implement—yet hide—some of their members from the base classes.
Q&A
Q Is it important to understand interfaces?
AYes. You will find interfaces used through C# programming. Many of the preconstructed methods provided within the class libraries include the use of interfaces.
438 |
Day 12 |
QYou said that the as and is keywords can be used with interfaces, yet you did not show an example. How does the use of these keywords differ from what was shown with classes?
AThe use of as and is with interfaces is nearly identical to their use with classes. Because the use is so similar, the coding examples with interfaces would be virtually the same as what was shown on Day 11.
Workshop
The Workshop provides quiz questions to help you solidify your understanding of the material covered and exercises to provide you with experience in using what you’ve learned. Try to understand the quiz and exercise answers before continuing to tomorrow’s lesson. Answers are provided on the CD.
Quiz
1.Are interfaces a reference type or a value type?
2.What is the purpose of an interface?
3.Are members of an interface declared as public, private, or protected?
4.What is the primary difference between an interface and a class?
5.What inheritance is available with structures?
6.What types can be included in an interface?
7.How would you declare a public class named AClass that inherits from a class named baseClass and implements an interface named IMyInterface?
8.How many classes can be inherited at one time?
9.How many interfaces can be inherited (implemented) at one time?
10.How is an explicit interface implementation done?
Exercises
1.Write the code for an interface named Iid that has a property named ID as its only member.
2.Write the code that would declare an interface named IPosition. This interface should contain a method that takes a Point value and returns a Boolean.
3.Bug Buster: The following code snippet might have a problem. If so, what is it?
public interface IDimensions
{
long Width;
Tapping into OOP: Interfaces |
439 |
long Height; double Area();
double Circumference(); int Sides();
}
4. Implement the IShape interface declared in Listing 12.1 into a class named
Rectangle.
12
WEEK 2
DAY 13
Making Your Programs React with Delegates, Events, and Indexers
You’ve learned many of the foundational topics related to C# programming. In today’s lesson, you learn about several additional topics that are foundational for your full understanding of C#. Today you…
•Learn about indexers.
•Build your own indexers.
•Explore delegates.
•Discover event programming.
•Create your own events and event handlers.
•Learn to multicast.
442 |
Day 13 |
Using an Indexer
On Day 7, “Storing Complex Stuff: Structures, Enumerators, and Arrays,” you learned about arrays. Today you learn about indexers. An indexer enables you to
use an index on an object to obtain values stored within the object. In essence, this enables you to treat an object like an array.
An indexer is also similar to a property. As with properties, you use get and set when defining an indexer. Unlike properties, you are not obtaining a specific data member; instead, you are obtaining a value from the object itself. When you define a property, you define a property name. With indexers, instead of creating a name as you do with properties, you use the this keyword, which refers to the object instance and, thus, the object name is used. The format for defining an indexer is shown here:
public dataType this[int index]
{
get
{
// Do whatever you want...
return aValue;
}
set
{
//Do whatever you want
//Generally you should set a value within the class
//based on the index and the value they assign.
}
}
Creating an indexer enables you to use bracket notation ([]) with an object to set and get a value from an object. As you can see in the format shown earlier, you state the dataType that will be set and returned by the indexer. In the get section, you return a value that is of dataType. In the set block, you can do something with a value of dataType.
As with properties and member functions, you can use the value keyword. This is the value passed as the argument to the set routine. The best way to understand all of this is to take a look at a simple example of using an indexer, as shown in Listing 13.1.
LISTING 13.1 Indexer.cs—Using an Indexer
1:// Indexer.cs - Using an indexer
2://--------------------------------------------------------------------
4: using System; 5:
6: public class SpellingList
Making Your Programs React with Delegates, Events, and Indexers |
443 |
LISTING 13.1 continued
7:{
8:protected string[] words = new string[size];
9:static public int size = 10;
10:
11:public SpellingList()
12:{
13:for (int x = 0; x < size; x++ )
14:words[x] = String.Format(“Word{0}”, x);
15:}
16:
17:public string this[int index]
18:{
19:get
20:{
21:string tmp;
22:
23:if( index >= 0 && index <= size-1 )
24: |
tmp = words[index]; |
25:else
26: |
tmp = “”; |
27: |
|
28:return ( tmp );
29:}
30:set
31:{
32:if( index >= 0 && index <= size-1 )
33: words[index] = value;
34:}
35:}
36:}
37:
38:public class Indexer
39:{
40:public static void Main()
41:{
42:SpellingList myList = new SpellingList();
43: |
13 |
|
44:myList[3] = “=====”;
45:myList[4] = “Brad”;
46:myList[5] = “was”;
47:myList[6] = “Here!”;
48:myList[7] = “=====”;
50:for ( int x = 0; x < SpellingList.size; x++ )
51:Console.WriteLine(myList[x]);
52:}
53:}
444 |
Day 13 |
Word0
Word1
Word2
=====
Brad was Here!
=====
Word8
Word9
This listing creates an indexer to be used with the SpellingList class. This class contains an array of strings named words that can be used to store a list of words.
This list is set to the size of the variable declared in Line 9.
Lines 11–15 contain a constructor for SpellingList that sets initial values into each element of the array. You could just as easily have requested the words from the reader or read them from a file. This constructor assigns the string value Word## to each of the elements in the array, where ## is the element number of the array.
Jumping down to the Indexer class in Lines 38–53, you see how the SpellingList class will be used. In Line 42, the SpellingList class is used to instantiate the myList object that will hold the words. Line 42 also causes the constructor to be executed. This initializes the Word## values. Lines 44–48 then change some of these values.
If you think back to how you worked with arrays, you should be saying, “wait a minute” as you look at Lines 44–48. To access the value of one of the words, you would normally have to access the data member within the object. When using arrays as a data member, you learned that you would assign a value to the fourth element as follows:
MyList.words[3] = “=====”;
Line 44, however, is accessing the fourth element within the object, which has been set to be the fourth element in the words array.
An indexer has been created for the SpellingList class in Lines 17–35. This indexer enables you to access the elements within the words array using just the object name.
Line 17 is the defining line for the indexer. You know that this is an indexer rather than a property because the this keyword is used instead of a name. Additionally, this is given an index (named index). The indexer will return a string value.
Lines 19–29 contain the get portion of the indexer. The get block returns a value based on the index. In this class, this value is an element from the words array. You can return any value that you want, but the value should make sense. In Line 23, a check is done to make sure that the index value is valid. If you don’t check the value of the index, you risk
Making Your Programs React with Delegates, Events, and Indexers |
445 |
having an exception thrown. In this listing, if the index is out of the range, a null value is returned (in Line 26). If the index is valid, the value stored in the words array at the index location will be returned.
The set portion of the indexer is in Lines 30–34. This block can be used to set information within the object. As with properties, the value keyword contains the value being assigned. In this code, the index value is again checked to make sure it is valid. If it is, a word in the words array is updated at the index location with the value assigned.
Looking again at the test class, you see that the set indexer block is used to assign values in Lines 44–48. For Line 44, the set indexer block will be called with value equal to ===== and will be passed with an index value of 3. In Line 45, value is Brad and the index is 4. In Line 51, the get indexer block is called with an index value of x. The value returned will be the string value returned by the get indexer block.
Tip
You should use indexers when it makes your code more readable and easier to understand. For example, you can create a stack that can have items placed on it and taken off of it. Using an indexer, you could access items within the stack.
Exploring Delegates
NEW TERM |
You now will learn about a more advanced topic: delegates. A delegate is a refer- |
|
ence type that defines the signature for a method call. The delegate can then |
|
|
|
|
|
|
|
|
accept and execute methods that are of the same format as this signature. |
|
|
You learned in yesterday’s lesson that an interface is a reference type that defines the |
|
|
layout for a class, but it does not itself define any of the functionality. Delegates are |
|
|
often compared to interfaces. A delegate defines the layout for a method but does not |
|
|
actually define a method. Instead, a delegate can accept and work with methods that |
13 |
|
match its layout (signature). |
An example will help make delegates clearer. In this example, a program will be created that sorts two numbers, either ascending or descending. The sorting direction is determined in the code presented; however, you could ask the reader to enter the direction of the sort. Based on the direction—ascending or descending—a different method will be used. Despite this, only one call, to a delegate, will be made. The delegate will be given the appropriate method to execute.
The format for declaring a delegate is shown here:
public delegate returnType DelegateName(parameters);
446 |
Day 13 |
Here, public can be replaced with appropriate access modifiers, and delegate is the keyword used to indicate that this is a delegate. The rest of the definition is for the signature of the method that the delegate will work with. As you should know from previous lessons, the signature includes the data type to be returned by the method (returnType), as well as the name of the parameters that will be received by the method (parameters). The name of the delegate goes where the method name would normally go. Because the delegate will be used to execute multiple methods that fit the return type and parameters, you don’t know the specific method names.
The example used here creates a delegate named Sort that can take multiple sorting methods. These methods will not return a value, so their return type will be void. The methods that will be used for sorting will pass in two integer variables that will be reference types. This enables the sorting functions to switch the values, if necessary. The delegate definition for the example is as follows:
public delegate void Sort(ref int a, ref int b);
Notice the semicolon at the end. Although this looks similar to a method definition, it is not a method definition. There is no body—a delegate is just a template for methods that can be executed. Methods are actually applied, or delegated, to this delegate to be executed.
A delegate is a template for multiple methods. For example, the Sort delegate in the example can be used with methods that don’t return a value and that take two reference integers as parameters. The following is an example of a method that can be used with the Sort delegate:
public static void Ascending( ref int first, ref int second )
{
if (first > second )
{
int tmp = first; first = second; second = tmp;
}
}
This method, Ascending, is of type void. Additionally, it receives two values that are both ref int. This matches the signature of the Sort delegate so that it can be used. This method takes the two values and checks to see whether the first is greater than the second. If it is, the two values are swapped using a simple sort routine. Because these values are ref types, the calling routine will have the values swapped as well.
Making Your Programs React with Delegates, Events, and Indexers |
447 |
A second method named Descending can also be created:
public static void Descending( ref int first, ref int second )
{
if (first < second )
{
int tmp = first; first = second; second = tmp;
}
}
This method is similar to Ascending, except that the larger value is kept in the first position. You could declare additional sort routines to use with this delegate, as long as the signature of your methods matches. Additionally, different programs could use the Sort delegate and have their own logic within their methods.
Now that a delegate has been declared and multiple functions can be used with it, what is next?
You need to associate your methods with the delegate. Instantiating delegate objects can do this. A delegate object is declared like other objects, with the parameter for the initializer being the method name that you are assigning (delegating) to the delegate. For example, to declare a delegate that can be used with the Ascending method, you code the following:
Sort up = new Sort(Ascending);
This creates a delegate object named up that can then be used. up is associated with the Ascending method that was declared. The following creates a Sort delegate associated with the Descending method. This delegate is called down.
Sort down = new Sort(Descending);
Now you’ve declared your delegate, created methods that can be used with it, and associated these methods to delegate objects. How do you get the delegated methods to exe-
cute? You create a method that receives a delegate object as a parameter. This generic 13 method can then execute the method from the delegate:
public void DoSort(Sort ar)
{
ar(ref val1, ref val2);
}
As you can see, the DoSort method receives a delegate named ar as its parameter.
This method then executes ar. You should notice that ar has the same signature as your
448 |
Day 13 |
delegate. If your delegate has a return type, ar will have a return type. The method of the ar call also matches your delegate. In essence, the DoSort method executes whichever method is passed as a Sort delegate object. For our example, if up is passed, ar(...) is equivalent to calling the Ascending method. If down is passed, ar(...) is equivalent to calling the Descending method.
You’ve now seen all the key pieces to working with a delegate. Listing 13.2 pulls the entire sample together into a workable solution.
LISTING 13.2 SortClass.cs—Using a Simple Delegate
1:// SortClass.cs - Using a delegates
2://--------------------------------------------------------------------
4: using System; 5:
6:public class SortClass
7:{
8:static public int val1;
9:static public int val2;
11: public delegate void Sort(ref int a, ref int b); 12:
13:public void DoSort(Sort ar)
14:{
15:ar(ref val1, ref val2);
16:}
17:}
18:
19:public class SortProgram
20:{
21:public static void Ascending( ref int first, ref int second )
22:{
23:if (first > second )
24:{
25:int tmp = first;
26:first = second;
27:second = tmp;
28:}
29:}
30: |
|
|
31: |
public static void Descending( ref int first, ref int second )32: |
{ |
33:if (first < second )
34:{
35:int tmp = first;
36:first = second;
37:second = tmp;
38:}