TOC

This article is currently in the process of being translated into Spanish (~36% done).

Clases:

Métodos con parámetros

En el artículo anterior, hablamos de métodos y tuvimos una breve introducción al concepto de parámetros de métodos/funciones. En este artículo, profundizaremos en este tema en todas sus variaciones. Como ya se ha mencionado, los métodos pueden funcionar sin parámetros, pero normalmente tendrán uno o más parámetros, que ayudarán al método a cumplir su tarea.

Ya vimos un caso de uso muy simple para los parámetros en el artículo anterior: Nuestro método AddNumbers(), que toma dos números como parámetros y devuelve la suma de estos dos números:

public int AddNumbers(int number1, int number2)
{
	return number1 + number2;
}

Esto demuestra en gran medida lo ingenioso que es el concepto de los métodos, porque con los métodos se puede encapsular la funcionalidad en el método, pero se puede modificar el resultado cuando se llama a este método a través de los parámetros:

AddNumbers(2, 3);

Result: 5

AddNumbers(38, 4);

Result: 42

Este es el tipo básico de parámetros, pero hablemos más sobre los diversos modificadores y opciones que se pueden usar para cambiar el comportamiento de los parámetros.

Tenga en cuenta que en este artículo, vamos a profundizar en todos los tipos de parámetros y en cómo pueden ayudarle, pero si acaba de empezar con C# y sólo quiere ver algunos resultados, lo siguiente podría ser un poco demasiado complejo y técnico por ahora. Siéntase libre de saltarse el resto del artículo y volver más tarde cuando esté listo.

Parámetros opcionales

Por defecto, cuando se llama a un método con uno o varios parámetros, estás obligado a suministrar valores para todos estos parámetros. Sin embargo, en algunas situaciones, es posible que tengas que hacer que uno o varios parámetros sean opcionales. En algunos lenguajes de programación, se le permitiría hacerlo simplemente marcando el parámetro como opcional, pero en C#, un parámetro se hace opcional suministrando un valor por defecto para él en la declaración del método. En realidad, ésta es una buena solución, porque te ahorra tener que escribir código adicional para hacer frente a situaciones en las que el parámetro no es suministrado por quien llama al método.

A continuación, un ejemplo de un método con un parámetro opcional:

public int AddNumbers(int number1, int number2, int number3 = 0)
{
	return number1 + number2 + number3;
}

El último parámetro (number3) es ahora opcional, porque hemos proporcionado un valor por defecto para él (0). Al llamarlo, ahora puede proporcionar dos o tres valores, de esta forma:

public void Main(string[] args)
{
	AddNumbers(38, 4);
	AddNumbers(36, 4, 2);
}

Puede hacer que más de un parámetro sea opcional - de hecho, su método puede consistir sólo en parámetros opcionales si es necesario. Sólo recuerde que los parámetros opcionales deben ser los últimos en la declaración del método - no los primeros o entre parámetros no opcionales.

El modificador params

Como alternativa a una serie de parámetros opcionales, se puede utilizar el modificador de params para permitir un número arbitrario de parámetros. Podría ser parecido a esto:

public void GreetPersons(params string[] names) { }

La llamada entonces podría quedar así:

GreetPersons("John", "Jane", "Tarzan");

Otra ventaja de usar el enfoque con params, es que también se le permite pasar cero parámetros al método. Los métodos con params también pueden aceptar parámetros normales, siempre y cuando el parámetro con el modificador params sea el último. Por otra parte, sólo se puede utilizar un parámetro con la palabra clave params por método. Éste es un ejemplo completo en el que usamos el modificador params para imprimir un número variable de nombres con nuestro método GreetPersons():

public void Main(string[] args)
{
	GreetPersons("John", "Jane", "Tarzan");
}

public void GreetPersons(params string[] names)
{
	foreach(string name in names)
		Console.WriteLine("Hello " + name);
}

Tipos de parámetros: por valor o por referencia

C#, al igual que otros lenguajes de programación, diferencia entre dos tipos de parámetros: "por valor" y "por referencia". El valor por defecto en C# es "por valor", lo que básicamente significa que cuando se pasa una variable en una llamada a un método, en realidad se está enviando una copia del objeto, en lugar de una referencia a él. Esto también significa que puedes hacer cambios en el parámetro desde dentro del método, sin afectar al objeto original que pasaste como parámetro.

Podemos demostrar fácilmente este comportamiento con un ejemplo:

public void Main(string[] args)
{
	int number = 20;
	AddFive(number);
	Console.WriteLine(number);
}

public void AddFive(int number)
{
	number = number + 5;
}

We have a very basic method called AddFive() which will add 5 to the number we pass to it. So in our Main() method, we create a variable called number with the value of 20 and then call the AddFive() method. Now on the next line, when we write out the number variable, one might expect that value is now 25, but instead, it remains 20. Why? Because by default, the parameter was passed in as a copy of the original object (by value), so when our AddFive() method worked on the parameter, it was working on a copy and thereby never affecting the original variable.

There are currently two ways of changing this behavior, so that our AddFive() method is allowed to modify the original value: We can use the ref modifier or the in/out modifiers.

The ref modifier

The ref modifier is short for "reference" and it basically just changes the behavior of the parameter from the default behavior of "by value" to "by reference", meaning that we are now passing in a reference to the original variable instead of a copy of its value. Here's a modified example:

public void Main(string[] args)
{
	int number = 20;
	AddFive(ref number);
	Console.WriteLine(number);
}

public void AddFive(ref int number)
{
	number = number + 5;
}

You will notice that I have added the "ref" keyword in two places: In the method declaration and when passing in the parameter in the method call. With this change, we now get the behavior we originally might have expected - the result is now 25 instead of 20, because the ref modifier allows the method to work on the actual value passed in as a parameter instead of a copy.

The out modifier

Just like the ref modifier, the out modifier ensures that the parameter is passed by reference instead of by value, but there's a major difference: When using the ref modifier, you pass in an initialized value which you may choose to modify inside the method, or leave it as it is. On the other hand, when using the out modifier, you are forced to initialize the parameter inside the method. This also means that you can pass in uninitialized values when using the out modifier - the compiler will complain if you are trying to leave a method with an out parameter without assigning a value to it.

In C#, a method can only return one value, but if you use the out modifier, you are able to circumvent this by passing in several parameters with the out modifier - when the method has been called, all out parameters will have been assigned. Here's an example, where we pass in two numbers and then, using out modifiers, return both an addition and a subtraction using these numbers:

public void Main(string[] args)
{
	int addedValue, subtractedValue;
	DoMath(10, 5, out addedValue, out subtractedValue);
	Console.WriteLine(addedValue);
	Console.WriteLine(subtractedValue);
}

public void DoMath(int number1, int number2, out int addedValue, out int subtractedValue)
{
	addedValue = number1 + number2;
	subtractedValue = number1 - number2;
}
Output:

15
5

As you can see in the beginning of the example, I declare the two variables (addedValue and subtractedValue) before passing them as out parameters. This was a requirement in previous versions of the C# language, but from C# version 6, you can declare them directly in the method call, like this:

DoMath(10, 5, out int addedValue, out int subtractedValue);
Console.WriteLine(addedValue);
Console.WriteLine(subtractedValue);

The in modifier

Just like the out modifier, the in modifier ensures that the parameter is passed as a reference instead of a copy of the value, but unlike the out modifier, the in modifier will prevent you from making any changes to the variable inside the method.

Your first thought might be: If I can't change the value of the parameter, then I might as well just pass it in as a regular parameter, where changes won't affect the original variable. And you're right, the result would appear to be exactly the same, but there's still a very valid reason for using the in modifier: By passing it as a reference instead of a value, you are saving resources because the framework doesn't have to spend time creating a copy of the object when passing it to the method like a regular parameter.

In most cases, this won't make much of a difference, because most parameters are simple strings or integers, but in situations where you repeatedly call the same method many times in a loop or where you pass in parameters with large values, e.g. very large strings or structs, this may give you a nice performance boost.

Here's an example where we use the in modifier:

public void Main(string[] args)
{
	string aVeryLargeString = "Lots of text...";
	InMethod(aVeryLargeString);
}

public void InMethod(in string largeString)
{
	Console.WriteLine(largeString);
}

In our method, InMethod(), the largeString parameter is now a read-only reference to the original variable (aVeryLargeString), so we're able to use it, but not modify it. If we try, the compiler will complain:

public void InMethod(in string largeString)
{
	largeString = "We can't do this...";
}

Error: Cannot assign to variable 'in string' because it is a readonly variable

Named parameters

As you have seen in all of the examples above, each parameter has a unique name in the method declaration. This allows you to reference the parameter inside the method. However, when calling the method, you don't use these names - you just supply the values in the same order as they are declared. This is not a problem for simple methods which takes 2-3 parameters, but some methods are more complex and requires more parameters. In those situations, it can be quite hard to figure out what the various values in a method call refers to, like in this example:

PrintUserDetails(1, "John", 42, null);

It's not very clear what the various parameters means in this method call, but if you look at the method declaration, you can see it:

public void PrintUserDetails(int userId, string name, int age = -1, List<string> addressLines = null)
{
	// Print details...
}

But it's annoying if you constantly have to look up the method declaration to understand what the parameters are doing, so for more complex methods, you can supply the parameter names directly in the method call. This also allows you to supply the parameter names in any order, instead of being forced to use the declaration order. Here's an example:

PrintUserDetails(name: "John Doe", userId: 1);

As an added bonus, this will allow you to supply a value for any of your optional parameters, without having to supply values for all previous optional parameters in the declaration. In other words, if you want to supply a value for the addressLines parameter in this example you would also have to provide a value for the age parameter, because it comes first in the method declaration. However, if you use named parameters, order no longer matters and you can just supply values for the required parameters as well as one or several of the optional parameters, e.g. like this:

PrintUserDetails(addressLines: new List<string>() { }, name: "Jane Doe", userId: 2);

Here's the complete example of using named parameters:

public void Main(string[] args)
{
	PrintUserDetails(1, "John", 42, null);
	PrintUserDetails(name: "John Doe", userId: 1);
	PrintUserDetails(addressLines: new List<string>() { }, name: "Jane Doe", userId: 2);
}

public void PrintUserDetails(int userId, string name, int age = -1, List<string> addressLines = null)
{
	// Print details...
	Console.WriteLine(name + " is " + age + " years old...");
}

Summary

As you can see from this article, parameters come in many forms and types. Fortunately, you'll come along way with plain, old regular parameters, but once you start to dig deeper into the C# language, you will benefit from knowing all the types and modifiers as explained in this article.

This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!