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);-20La 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:
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); }}No hay dinero suficiente: María50- Llamas al método sobre un objeto con un punto:
account.Withdraw(70), igual que a los campos. - Dentro,
Balancees el saldo de esta cuenta, el objeto que está antes del punto. También puedes escribirthis.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
- Dos cuentas, dos objetos. Cada uno tiene su propio saldo.
- maria.Withdraw(70): this recibe una flecha al objeto que está antes del punto.
- En el método, el saldo es el de this: 100 - 70 = 30.
- El mismo método, otro objeto: ahora this apunta a Pablo.
- 70 es más que 40: el método imprime un rechazo, y el saldo de Pablo no cambia.
- 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 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);Cada objeto tiene su propio saldo. maria.Deposit(30) cambia solo el saldo de María, y peter.Withdraw(10) cambia solo el de Pablo. Pablo tiene 40, así que retirar 10 sale bien.
Pruébalo
Encuentra el error
Elige la línea con el error.
static delante del método significa que no tiene objeto. Entonces tampoco tiene un saldo que ver. El compilador se detiene en la línea del saldo:
error CS0120: An object reference is required for the non-static field, method, or property 'Account.Balance'Pero el error está en la cabecera del método: quita static.
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 + " €");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);ProductC# 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);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);"Leyendo: " + book tiene que convertir book en texto. Así que se llama al ToString() del libro, y devuelve el título y el autor.Ejercicio
Objetos que hacen algo
Resuelve las tareas en tu editor. Toma las clases de las lecciones anteriores y añádeles métodos.
Person: un métodoIntroduce()que imprime el nombre y la edad.SimpleCar: un métodoAccelerate()que aumenta la velocidad en 10. Ahora Main no toca la velocidad directamente.DanceMove: un métodoPerform()que «ejecuta» el paso.Book: un métodoRead()que imprime «Leyendo [título] de [autor]».
Crea un objeto de cada clase y llama a sus métodos:
Hola, soy María y tengo 34 añosVelocidad tras acelerar: 10Ejecuto moonwalk durante 5 segundosLeyendo Cien años de soledad de Gabriel García MárquezEjercicio
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.
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);Velocidad: 20Toyota: 100 km, 14 lNo hay combustible suficiente para 300 kmToyota: 100 km, 50 lToyota: 400 km, 32 ldistance * 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
Productun métodoApplyDiscount(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
Accountun contador de operaciones: cada depósito y cada retiro que sale bien lo aumenta en 1. Un métodoToString()devuelve el titular, el saldo y el número de operaciones.
Test
Ponte a prueba
Lecciones relacionadas
Sección «Lecciones relacionadas»- Se basa en: Métodos · Parámetros y return · Clases y objetos · Constructores y this
- Continúa en: Encapsulamiento · static: lo que comparten todos los objetos · Herencia · Polimorfismo
- Ejercicios sobre el tema: Clases y objetos