Ir al contenido
wannacode

Métodos de instancia

En resumen: Un método de instancia (sin static) trabaja con los datos del objeto concreto que está antes del punto, y ToString dice cómo se imprime el objeto.

Problema

Cada uno retira a su manera

El banco tiene una clase Account con un titular y un saldo. En el programa se retira dinero en dos lugares. El cajero comprueba que haya dinero suficiente. El pago con tarjeta lo escribió otra persona, y no comprueba nada:

Account account = new Account("María", 100);
// El cajero comprueba que haya dinero suficiente.
if (account.Balance >= 70)
{
account.Balance -= 70;
}
// El pago con tarjeta lo escribió otra persona, y no comprueba nada.
account.Balance -= 50;
Console.WriteLine(account.Balance);
Salida
-20

La cuenta está por debajo de cero. La regla «no puedes retirar más de lo que tienes» es importante, pero se escribe (o se olvida) por separado en cada lugar que retira.

La idea

Los métodos viven con los datos

La lógica del retiro es parte de la cuenta, así que ponla en la clase Account. Un método de la clase, sin static, es un método de instancia:

Program.cs
class Account
{
public string Owner;
public double Balance;
public Account(string owner, double balance)
{
Owner = owner;
Balance = balance;
}
public void Deposit(double amount)
{
Balance += amount;
}
public void Withdraw(double amount)
{
if (amount <= Balance)
{
Balance -= amount;
}
else
{
Console.WriteLine("No hay dinero suficiente: " + Owner);
}
}
}
class Program
{
static void Main()
{
Account account = new Account("María", 100);
account.Withdraw(70);
account.Withdraw(50);
account.Deposit(20);
Console.WriteLine(account.Balance);
}
}
Salida
No hay dinero suficiente: María
50
  • Llamas al método sobre un objeto con un punto: account.Withdraw(70), igual que a los campos.
  • Dentro, Balance es el saldo de esta cuenta, el objeto que está antes del punto. También puedes escribir this.Balance.
  • La comprobación se escribe una vez. Todo el que retira a través del método pasa por ella.

Account maria = new Account("María", 100);
Account peter = new Account("Pablo", 40);
maria.Withdraw(70);
peter.Withdraw(70);
Console.WriteLine(maria.Balance + " " + peter.Balance);
Paso a paso, en palabras
  1. Dos cuentas, dos objetos. Cada uno tiene su propio saldo.
  2. maria.Withdraw(70): this recibe una flecha al objeto que está antes del punto.
  3. En el método, el saldo es el de this: 100 - 70 = 30.
  4. El mismo método, otro objeto: ahora this apunta a Pablo.
  5. 70 es más que 40: el método imprime un rechazo, y el saldo de Pablo no cambia.
  6. El código del método existe una sola vez. Los campos son del objeto sobre el que lo llamas.

El pago con tarjeta ya no puede «olvidar» la comprobación si retira con Withdraw. Pero todavía puede escribir directamente en el campo. Cómo impedirlo lo verás en la lección «Encapsulamiento».

La idea

Método de instancia o método estático

Los métodos del módulo «Métodos» eran static. Lo reciben todo como parámetros y no tienen un objeto propio. Un método de instancia trabaja con los campos del objeto sobre el que se llamó:

static double ApplyDiscount(double price, double percent)
{
return price * (100 - percent) / 100;
}
double price = ApplyDiscount(10, 10);
account.Withdraw(70);

No puedes llamar a un método de instancia a través del nombre de la clase. No está claro de qué cuenta retirar:

Account.Withdraw(70);
Error de compilación
error CS0120: An object reference is required for the non-static field, method, or property 'Account.Withdraw(double)'

Qué significa exactamente static y cuándo lo necesitas viene en la lección «static: lo que comparten todos los objetos».

Pruébalo

¿De quién es la cuenta?

¿Qué imprimirá el programa?

Account maria = new Account("María", 100);
Account peter = new Account("Pablo", 40);
maria.Deposit(30);
peter.Withdraw(10);
Console.WriteLine(maria.Balance + " " + peter.Balance);
Respuesta

Pruébalo

Encuentra el error

La clase no compila. ¿Qué línea tiene el error?

Elige la línea con el error.

La idea

Un método también puede responder

Un método de instancia puede devolver un resultado, igual que los métodos del módulo «Métodos». Un producto conoce su precio y su cantidad, así que puede decirte lo que cuesta:

class Product
{
// los campos y el constructor, como antes
public double Total()
{
return Price * Quantity;
}
}
Product[] cart =
{
new Product("Pan", 1.20, 2),
new Product("Leche", 2.10, 1),
new Product("Café", 7.50, 1)
};
double sum = 0;
foreach (Product p in cart)
{
sum += p.Total();
}
Console.WriteLine("Total: " + sum + " €");
Salida
Total: 12 €

Compáralo con la primera lección del módulo: allí la suma se calculaba en Main a partir de los campos. Ahora Main solo le pregunta a cada producto. Si mañana se añade un descuento por cantidad, cambias solo Total().

La idea

¿Cómo se ve el objeto como texto?

Si imprimes un producto directamente, obtienes algo inútil:

Product coffee = new Product("Café", 7.50, 1);
Console.WriteLine(coffee);
Salida
Product

C# imprime solo el nombre de la clase.

La clase puede decir cómo debe mostrarse. El método se llama ToString(), y devuelve un texto:

class Product
{
// los campos y el constructor, como antes
public double Total()
{
return Price * Quantity;
}
public override string ToString()
{
return Name + " x" + Quantity + ": " + Total() + " €";
}
}
Product coffee = new Product("Café", 7.50, 1);
Product bread = new Product("Pan", 1.20, 2);
Console.WriteLine(coffee);
Console.WriteLine("En el carrito: " + bread);
Salida
Café x1: 7.5 €
En el carrito: Pan x2: 2.4 €

No lo llamas tú. Console.WriteLine(coffee) y "En el carrito: " + bread lo llaman por ti cuando el objeto tiene que convertirse en texto.

Por ahora escribe override en la cabecera exactamente así. Qué significa viene en el módulo «Herencia y polimorfismo».

Pruébalo

Un libro en una frase

¿Qué imprimirá el programa?

class Book
{
public string Title;
public string Author;
public Book(string title, string author)
{
Title = title;
Author = author;
}
public override string ToString()
{
return Title + " de " + Author;
}
}
Book book = new Book("Rayuela", "Julio Cortázar");
Console.WriteLine("Leyendo: " + book);
Respuesta

Ejercicio

Objetos que hacen algo

Resuelve las tareas en tu editor. Toma las clases de las lecciones anteriores y añádeles métodos.

  1. Person: un método Introduce() que imprime el nombre y la edad.
  2. SimpleCar: un método Accelerate() que aumenta la velocidad en 10. Ahora Main no toca la velocidad directamente.
  3. DanceMove: un método Perform() que «ejecuta» el paso.
  4. Book: un método Read() que imprime «Leyendo [título] de [autor]».

Crea un objeto de cada clase y llama a sus métodos:

Salida
Hola, soy María y tengo 34 años
Velocidad tras acelerar: 10
Ejecuto moonwalk durante 5 segundos
Leyendo Cien años de soledad de Gabriel García Márquez

Ejercicio

Un coche con combustible

Una clase Car con una marca, una velocidad, combustible (en litros) y un kilometraje. El constructor recibe la marca y el combustible. La velocidad y el kilometraje empiezan en 0.

  • Accelerate() aumenta la velocidad en 10.
  • Refuel(double liters) añade combustible, pero en el depósito caben como mucho 50 litros.
  • Drive(int distance) gasta 6 litros cada 100 km y suma la distancia al kilometraje. Si no hay combustible suficiente, imprime un mensaje y el coche no se mueve.
  • ToString() devuelve la marca, el kilometraje y el combustible.
Main
Car car = new Car("Toyota", 20);
car.Accelerate();
car.Accelerate();
Console.WriteLine("Velocidad: " + car.Speed);
car.Drive(100);
Console.WriteLine(car);
car.Drive(300);
car.Refuel(40);
Console.WriteLine(car);
car.Drive(300);
Console.WriteLine(car);
Salida
Velocidad: 20
Toyota: 100 km, 14 l
No hay combustible suficiente para 300 km
Toyota: 100 km, 50 l
Toyota: 400 km, 32 l
Pista: distance * 6 / 100.0. Con 100 en lugar de 100.0, la división es entera, y para 30 km obtienes 1 litro en lugar de 1,8.

Más tareas

  • Descuento. Añade a Product un método ApplyDiscount(double percent) que baja el precio del producto. Compáralo con el método estático del módulo «Métodos» que devolvía un precio nuevo.
  • Una cuenta con historial. Añade a Account un contador de operaciones: cada depósito y cada retiro que sale bien lo aumenta en 1. Un método ToString() devuelve el titular, el saldo y el número de operaciones.

Test

Ponte a prueba

1. ¿Qué hace maria.Deposit(30)?
2. ¿Qué puede ver un método de instancia que un método estático no puede?
3. ¿Por qué Account.Withdraw(70); no compila?
4. ¿Cuándo se llama ToString() por sí solo?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·