Home Random Page


CATEGORIES:

BiologyChemistryConstructionCultureEcologyEconomyElectronicsFinanceGeographyHistoryInformaticsLawMathematicsMechanicsMedicineOtherPedagogyPhilosophyPhysicsPolicyPsychologySociologySportTourism






Nbsp;   Passing a Variable Number of Arguments to a Method

It’s sometimes convenient for the developer to define a method that can accept a variable number of arguments. For example, the System.String type offers methods allowing an arbitrary number of strings to be concatenated together and methods allowing the caller to specify a set of strings that are to be formatted together.

To declare a method that accepts a variable number of arguments, you declare the method as follows.

 

static Int32 Add(params Int32[] values) {

// NOTE: it is possible to pass the 'values'

// array to other methods if you want to.

 

Int32 sum = 0;

if (values != null) {

for (Int32 x = 0; x < values.Length; x++) sum += values[x];

}

return sum;

}

 

Everything in this method should look very familiar to you except for the params keyword that is applied to the last parameter of the method signature. Ignoring the params keyword for the mo- ment, it’s obvious that this method accepts an array of Int32 values and iterates over the array, add- ing up all of the values. The resulting sum is returned to the caller.


Obviously, code can call this method as follows.

 

public static void Main() {

// Displays "15"

Console.WriteLine(Add(new Int32[] { 1, 2, 3, 4, 5 } ));

}

 

It’s clear that the array can easily be initialized with an arbitrary number of elements and then passed off to Add for processing. Although the preceding code would compile and work correctly, it is a little ugly. As developers, we would certainly prefer to have written the call to Add as follows.

 

public static void Main() {

// Displays "15"

Console.WriteLine(Add(1, 2, 3, 4, 5));

}

 

You’ll be happy to know that we can do this because of the params keyword. The params keyword tells the compiler to apply an instance of the System.ParamArrayAttribute custom attribute to the parameter.

When the C# compiler detects a call to a method, the compiler checks all of the methods with the specified name, where no parameter has the ParamArray attribute applied. If a method exists that can accept the call, the compiler generates the code necessary to call the method. However, if the compiler can’t find a match, it looks for methods that have a ParamArray attribute to see whether the call can be satisfied. If the compiler finds a match, it emits code that constructs an array and populates its elements before emitting the code that calls the selected method.

In the previous example, no Add method is defined that takes five Int32-compatible arguments; however, the compiler sees that the source code has a call to Add that is being passed a list of Int32 values and that there is an Add method whose array-of-Int32 parameter is marked with the Param­ Array attribute. So the compiler considers this a match and generates code that coerces the param- eters into an Int32 array and then calls the Add method. The end result is that you can write the code, easily passing a bunch of parameters to Add, but the compiler generates code as though you’d written the first version that explicitly constructs and initializes the array.



Only the last parameter to a method can be marked with the params keyword (ParamArray­ Attribute). This parameter must also identify a single-dimension array of any type. It’s legal to pass null or a reference to an array of 0 entries as the last parameter to the method. The follow- ing call to Add compiles fine, runs fine, and produces a resulting sum of 0 (as expected).

 

public static void Main() {

// Both of these lines display "0" Console.WriteLine(Add()); // passes new Int32[0] to Add

Console.WriteLine(Add(null)); // passes null to Add: more efficient (no array allocated)

}


So far, all of the examples have shown how to write a method that takes an arbitrary number of Int32 parameters. How would you write a method that takes an arbitrary number of parameters where the parameters could be any type? The answer is very simple: just modify the method’s proto- type so that it takes an Object[] instead of an Int32[]. Here’s a method that displays the Type of every object passed to it.

 

public sealed class Program { public static void Main() {

DisplayTypes(new Object(), new Random(), "Jeff", 5);

}

 

private static void DisplayTypes(params Object[] objects) { if (objects != null) {

foreach (Object o in objects) Console.WriteLine(o.GetType());

}

}

}

 

Running this code yields the following output.

 

System.Object System.Random System.String System.Int32

 

 

ImportantBe aware that calling a method that takes a variable number of arguments in- curs an additional performance hit unless you explicitly pass null. After all, an array object

must be allocated on the heap, the array’s elements must be initialized, and the array’s memory must ultimately be garbage collected. To help reduce the performance hit associ- ated with this, you may want to consider defining a few overloaded methods that do not use the params keyword. For some examples, look at the System.String class’s Concat method, which has the following overloads.

public sealed class String : Object, ... { public static string Concat(object arg0);

public static string Concat(object arg0, object arg1);

public static string Concat(object arg0, object arg1, object arg2); public static string Concat(params object[] args);

 

public static string Concat(string str0, string str1);

public static string Concat(string str0, string str1, string str2);

public static string Concat(string str0, string str1, string str2, string str3); public static string Concat(params string[] values);

}

As you can see, the Concat method defines several overloads that do not use the params keyword. These versions of the Concat method are the most frequently called overloads, and these overloads exist in order to improve performance for the most common scenar- ios. The overloads that use the params keyword are there for the less common scenarios; these scenarios will suffer a performance hit, but fortunately, they are rare.


 
 

Parameter and Return Type Guidelines

When declaring a method’s parameter types, you should specify the weakest type possible, prefer- ring interfaces over base classes. For example, if you are writing a method that manipulates a col- lection of items, it would be best to declare the method’s parameter by using an interface such as IEnumerable<T> rather than using a strong data type such as List<T> or even a stronger interface type such as ICollection<T> or IList<T>.

 

// Desired: This method uses a weak parameter type

public void ManipulateItems<T>(IEnumerable<T> collection) { ... }

 

// Undesired: This method uses a strong parameter type public void ManipulateItems<T>(List<T> collection) { ... }

 

The reason, of course, is that someone can call the first method passing in an array object, a List<T> object, a String object, and so on—any object whose type implements IEnumerable<T>. The second method allows only List<T> objects to be passed in; it will not accept an array or a String object. Obviously, the first method is better because it is much more flexible and can be used in a much wider range of scenarios.

Naturally, if you are writing a method that requires a list (not just any enumerable object), then you should declare the parameter type as an IList<T>. You should still avoid declaring the parameter type as List<T>. Using IList<T> allows the caller to pass arrays and any other objects whose type implements IList<T>.

Note that my examples talked about collections, which are designed using an interface archi- tecture. If we were talking about classes designed using a base class architecture, the concept still applies. So, for example, if I were implementing a method that processed bytes from a stream, we’d have the following.

 

// Desired: This method uses a weak parameter type public void ProcessBytes(Stream someStream) { ... }

 

// Undesired: This method uses a strong parameter type public void ProcessBytes(FileStream fileStream) { ... }

 

The first method can process bytes from any kind of stream: a FileStream, a NetworkStream, a MemoryStream, and so on. The second method can operate only on a FileStream, making it far more limited.

On the flip side, it is usually best to declare a method’s return type by using the strongest type possible (trying not to commit yourself to a specific type). For example, it is better to declare a method that returns a FileStream object as opposed to returning a Stream object.

 

// Desired: This method uses a strong return type public FileStream OpenFile() { ... }

 

// Undesired: This method uses a weak return type public Stream OpenFile() { ... }


Here, the first method is preferred because it allows the method’s caller the option of treating

the returned object as either a FileStream object or as a Stream object. Meanwhile, the second method requires that the caller treat the returned object as a Stream object. Basically, it is best to let the caller have as much flexibility as possible when calling a method, allowing the method to be used in the widest range of scenarios.

Sometimes you want to retain the ability to change the internal implementation of a method with- out affecting the callers. In the example just shown, the OpenFile method is unlikely to ever change its internal implementation to return anything other than a FileStream object (or an object whose type is derived from FileStream). However, if you have a method that returns a List<String> ob- ject, you might very well want to change the internal implementation of this method in the future so that it would instead return a String[]. In the cases in which you want to leave yourself some flex- ibility to change what your method returns, choose a weaker return type. The following is an example.

 

// Flexible: This method uses a weaker return type public IList<String> GetStringCollection() { ... }

 

// Inflexible: This method uses a stronger return type public List<String> GetStringCollection() { ... }

 

In this example, even though the GetStringCollection method uses a List<String> ob- ject internally and returns it, it is better to prototype the method as returning an IList<String> instead. In the future, the GetStringCollection method could change its internal collection to use a String[], and callers of the method won’t be required to change any of their source code. In fact, they won’t even have to recompile their code. Notice in this example that I’m using the strongest of the weakest types. For instance, I’m not using an IEnumerable<String> or even ICollection<String>.

 

 


Date: 2016-03-03; view: 746


<== previous page | next page ==>
Nbsp;   Passing Parameters by Reference to a Method | Nbsp;   Parameterless Properties
doclecture.net - lectures - 2014-2024 year. Copyright infringement or personal data (0.01 sec.)