Entender los modificadores de acceso de C#
En este artículo, nos sumergiremos en los modificadores de acceso de C#, que definen la visibilidad y accesibilidad de tipos, métodos y variables en C#. Tim Corey, en su vídeo "C# Access Modifiers (beyond public and private) - what they are, how to use them, and best practices", explica varios modificadores de acceso y demuestra su uso práctico en una aplicación de consola.
En este artículo se explica lo que ha tratado Tim, junto con ejemplos de código para mejorar la comprensión. Las marcas de tiempo proporcionadas permiten seguir el vídeo para una experiencia más práctica.
¿Qué son los modificadores de acceso?
Introducción
Tim Corey comienza presentando los modificadores de acceso, explicando que determinan quién puede ver y utilizar un recurso en C#. Aunque estamos familiarizados con los modificadores públicos y privados de uso común, Tim explora seis modificadores de acceso diferentes y sus casos de uso.
Explicación de la aplicación demo
Tim crea una aplicación sencilla para demostrar cómo funcionan varios modificadores de acceso. La aplicación consta de una interfaz de usuario de consola y una biblioteca de demostración, ambas en .NET Framework.
Estructura del proyecto:
- UI de consola: Una aplicación de consola para probar modificadores de acceso.
- Demo Library: Una biblioteca de clases donde se demuestran diferentes modificadores de acceso.
public class AccessDemo
{
private void PrivateDemo() { }
internal void InternalDemo() { }
public void PublicDemo() { }
}
public class AccessDemo
{
private void PrivateDemo() { }
internal void InternalDemo() { }
public void PublicDemo() { }
}
1. Privado
Qué es:
El modificador private restringe el acceso al método, campo o propiedad únicamente dentro de la clase en la que está definido.
Ejemplo de código:
public class AccessDemo
{
private void PrivateDemo()
{
Console.WriteLine("Private method can only be accessed within this class.");
}
public void CallPrivateDemo()
{
PrivateDemo(); // Works because it's within the same class
}
}
public class AccessDemo
{
private void PrivateDemo()
{
Console.WriteLine("Private method can only be accessed within this class.");
}
public void CallPrivateDemo()
{
PrivateDemo(); // Works because it's within the same class
}
}
Explicación: El método PrivateDemo solo es accesible dentro de la clase AccessDemo. En el vídeo, Tim demuestra que no se puede acceder desde fuera de la clase, aunque haya otras clases en el mismo proyecto.
Best práctica: Utiliza private cuando quieras restringir el acceso al funcionamiento interno de tu clase, asegurando que no pueda ser alterada directamente desde otras partes de tu aplicación.
2. Interno
Qué es:
El modificador interno permite acceder al método o propiedad únicamente dentro del mismo ensamblado (el proyecto). Se trata de un modificador de acceso más amplio que el privado, ya que incluye todas las clases de un mismo proyecto.
Ejemplo de código:
public class AccessDemo
{
internal void InternalDemo()
{
Console.WriteLine("Internal method is accessible within the same assembly.");
}
}
public class AccessDemo
{
internal void InternalDemo()
{
Console.WriteLine("Internal method is accessible within the same assembly.");
}
}
Explicación: El método InternalDemo puede ser accedido por cualquier clase dentro del mismo ensamblado, pero no desde otros ensamblados. En el vídeo, Tim muestra que internal permite el acceso dentro del mismo proyecto, pero deniega el acceso desde fuera.
Práctica recomendada: Utilice internal para métodos o propiedades que estén destinados a ser utilizados únicamente dentro del ensamblado actual, como funciones de ayuda o utilidades que no deberían exponerse a proyectos externos.
3. Público
Qué es:
El modificador public permite acceder al método o propiedad desde cualquier otra clase o ensamblado. Este es el nivel de acceso más permisivo.
Ejemplo de código:
public class AccessDemo
{
public void PublicDemo()
{
Console.WriteLine("Public method can be accessed from any class.");
}
}
public class AccessDemo
{
public void PublicDemo()
{
Console.WriteLine("Public method can be accessed from any class.");
}
}
Explicación: El método PublicDemo es accesible desde cualquier lugar, incluyendo otras clases en el mismo ensamblado u otros ensamblados. Tim demuestra que public es el modificador de acceso más común, especialmente cuando se exponen métodos en bibliotecas.
Práctica recomendada: Utilice public para métodos y propiedades que deban ser accesibles por otras partes de la aplicación o proyectos externos, como puntos finales de API o utilidades de uso generalizado.
4. Protegido
Qué es:
El modificador protected permite acceder al método o propiedad dentro de la clase en la que está definido y en cualquier clase derivada (herencia). Este modificador es útil para la programación orientada a objetos, especialmente en casos de herencia.
Ejemplo de código:
public class AccessDemo
{
protected void ProtectedDemo()
{
Console.WriteLine("Protected method can be accessed within the class and derived classes.");
}
}
public class DerivedClass : AccessDemo
{
public void CallProtectedDemo()
{
ProtectedDemo(); // Accessible because of inheritance
}
}
public class AccessDemo
{
protected void ProtectedDemo()
{
Console.WriteLine("Protected method can be accessed within the class and derived classes.");
}
}
public class DerivedClass : AccessDemo
{
public void CallProtectedDemo()
{
ProtectedDemo(); // Accessible because of inheritance
}
}
Explicación: El método ProtectedDemo puede ser accedido desde la clase AccessDemo y cualquier clase que herede de ella. Tim explica que protected es menos común pero muy útil cuando se trabaja con herencia.
Best práctica: Utilice protected cuando desee permitir que las clases derivadas tengan acceso a métodos o propiedades específicos, pero no desee que sean accesibles fuera de la jerarquía de clases.
5. Privado Protegido
Qué es:
El modificador private protected combina las reglas de private y protected. Restringe el acceso a métodos o propiedades dentro de la clase definidora y las clases derivadas dentro del mismo ensamblado. Esto significa que ofrece un mayor nivel de protección y un límite más estricto para el control de acceso basado en la herencia en comparación con el protegido.
Ejemplo de código:
public class AccessDemo
{
private protected void PrivateProtectedDemo()
{
Console.WriteLine("Private Protected method can be accessed within the same assembly and derived classes.");
}
}
public class DerivedClass : AccessDemo
{
public void CallPrivateProtectedDemo()
{
PrivateProtectedDemo(); // Accessible because of inheritance within the same assembly
}
}
public class UnrelatedClass
{
public void TestAccess()
{
// PrivateProtectedDemo(); // Error: Not accessible in unrelated classes
}
}
public class AccessDemo
{
private protected void PrivateProtectedDemo()
{
Console.WriteLine("Private Protected method can be accessed within the same assembly and derived classes.");
}
}
public class DerivedClass : AccessDemo
{
public void CallPrivateProtectedDemo()
{
PrivateProtectedDemo(); // Accessible because of inheritance within the same assembly
}
}
public class UnrelatedClass
{
public void TestAccess()
{
// PrivateProtectedDemo(); // Error: Not accessible in unrelated classes
}
}
Explicación: El método PrivateProtectedDemo es accesible en la DerivedClass porque hereda de AccessDemo y existe en el mismo ensamblado. Sin embargo, no se puede acceder en UnrelatedClass, ya que esta clase no hereda de AccessDemo.
Práctica recomendada: Utilice el modificador de acceso protegido privado con moderación cuando necesite controlar estrictamente el acceso a la herencia dentro del mismo ensamblado. Esto es especialmente útil en situaciones en las que la exposición de métodos o propiedades a través de ensamblados puede comprometer la encapsulación.
6. Protección interna
Qué es:
El modificador interno protegido combina los niveles de modificador de acceso protegido e interno. Permite el acceso desde el mismo ensamblado o desde clases derivadas, aunque estén en otro ensamblado.
Ejemplo de código:
public class AccessDemo
{
protected internal void ProtectedInternalDemo()
{
Console.WriteLine("Protected Internal method can be accessed within the same assembly or from derived classes.");
}
}
public class DerivedClass : AccessDemo
{
public void CallProtectedInternalDemo()
{
ProtectedInternalDemo(); // Accessible due to inheritance
}
}
public class AccessDemo
{
protected internal void ProtectedInternalDemo()
{
Console.WriteLine("Protected Internal method can be accessed within the same assembly or from derived classes.");
}
}
public class DerivedClass : AccessDemo
{
public void CallProtectedInternalDemo()
{
ProtectedInternalDemo(); // Accessible due to inheritance
}
}
Explicación: El método ProtectedInternalDemo es accesible dentro del mismo ensamblado, y también desde cualquier clase derivada, independientemente del ensamblado.
Práctica recomendada: Utilice protected internal cuando desee exponer un método tanto a clases derivadas (en otros ensamblados) como a clases del mismo ensamblado, pero no a todo el mundo.
¿Por qué no hacerlo todo público?
Tim Corey explica la importancia de utilizar modificadores de acceso y por qué no todo debe ser público. Aunque hacerlo todo público puede parecer conveniente, introduce riesgos significativos, como filtraciones de datos, errores y confusión en el desarrollo. Los modificadores de acceso existen para proteger la información, evitar el acceso público no deseado y aportar claridad a las bases de código.
1. Proteger la información privada
Tim explica por qué no debe hacerse pública la información confidencial, como los números de la Seguridad Social o de las tarjetas de crédito. Demuestra un ejemplo de "mala clase" en el que la exposición de datos se produce debido al acceso público:
Mal ejemplo:
public class User
{
public string SSN; // Anyone can access and modify it directly
}
public class User
{
public string SSN; // Anyone can access and modify it directly
}
Buen ejemplo:
public class User
{
private string ssn;
public string GetMaskedSSN()
{
return "XXX-XX-" + ssn.Substring(ssn.Length - 4);
}
public void SetSSN(string value)
{
// Add validation if needed
ssn = value;
}
}
public class User
{
private string ssn;
public string GetMaskedSSN()
{
return "XXX-XX-" + ssn.Substring(ssn.Length - 4);
}
public void SetSSN(string value)
{
// Add validation if needed
ssn = value;
}
}
2. Protección de métodos privados
A partir de: 35:11\
Tim explica que los métodos privados ayudan a encapsular comportamientos que no deben ser accesibles directamente. Él usa el ejemplo de un método DeleteUser como parte de un proceso más grande como la salida de empleados.
Mal ejemplo:
public class UserManager
{
public void DeleteUser(int userId)
{
// Deletes the user without considering related processes
}
}
public class UserManager
{
public void DeleteUser(int userId)
{
// Deletes the user without considering related processes
}
}
Buen ejemplo:
public class UserManager
{
public void OffboardUser(int userId)
{
RevokeAccess(userId);
DeleteUser(userId); // Used privately as part of offboarding
}
private void DeleteUser(int userId)
{
// Internal logic to delete the user
}
private void RevokeAccess(int userId)
{
// Logic to revoke system access
}
}
public class UserManager
{
public void OffboardUser(int userId)
{
RevokeAccess(userId);
DeleteUser(userId); // Used privately as part of offboarding
}
private void DeleteUser(int userId)
{
// Internal logic to delete the user
}
private void RevokeAccess(int userId)
{
// Logic to revoke system access
}
}
3. Prevención de errores
Los modificadores de acceso evitan errores al garantizar que los datos se establecen o recuperan con la validación adecuada. Tim ilustra esto con un ejemplo que involucra una propiedad Age.
Mal ejemplo:
public class Person
{
public int Age; // Can be directly set to an invalid value
}
public class Person
{
public int Age; // Can be directly set to an invalid value
}
Buen ejemplo:
public class Person
{
private int age;
public int Age
{
get { return age; }
set
{
if (value < 0 || value > 120)
throw new ArgumentOutOfRangeException("Age must be between 0 and 120.");
age = value;
}
}
}
public class Person
{
private int age;
public int Age
{
get { return age; }
set
{
if (value < 0 || value > 120)
throw new ArgumentOutOfRangeException("Age must be between 0 and 120.");
age = value;
}
}
}
4. Reducir la confusión y aumentar la claridad
El uso correcto de los modificadores de acceso simplifica el desarrollo al exponer solo lo necesario, evitando confusiones. Por ejemplo, en una aplicación con miles de métodos, exponer solo los públicos garantiza que los desarrolladores vean únicamente las opciones relevantes.
Ejemplo:
public class MathLibrary
{
public int Add(int a, int b) => a + b;
public int Subtract(int a, int b) => a - b;
private void LogCalculation(string operation, int result)
{
// Logging is internal and not exposed
}
}
public class MathLibrary
{
public int Add(int a, int b) => a + b;
public int Subtract(int a, int b) => a - b;
private void LogCalculation(string operation, int result)
{
// Logging is internal and not exposed
}
}
5. Beneficios en aplicaciones o bibliotecas más grandes
Para proyectos a gran escala, Tim explica cómo el uso adecuado de modificadores de acceso garantiza que sólo se expongan las partes necesarias de una biblioteca, lo que reduce la carga cognitiva de los desarrolladores que utilizan la biblioteca.
Ejemplo:
public class MathLibrary
{
public int Add(int a, int b) => a + b;
public int Subtract(int a, int b) => a - b;
private void LogCalculation(string operation, int result)
{
// Logging is internal and not exposed
}
}
public class MathLibrary
{
public int Add(int a, int b) => a + b;
public int Subtract(int a, int b) => a - b;
private void LogCalculation(string operation, int result)
{
// Logging is internal and not exposed
}
}
Si utiliza correctamente estos modificadores de acceso, facilitará su trabajo y el de los demás.
Conclusión
Tim Corey proporciona una guía clara y práctica para dominar los modificadores de acceso de C#, mostrando cómo utilizarlos eficazmente para crear aplicaciones seguras, mantenibles y profesionales. Sus explicaciones detalladas y ejemplos del mundo real hacen que este tema sea accesible para desarrolladores de todos los niveles.
Para obtener información más detallada y ver estos conceptos en acción, asegúrate de ver el vídeo completo de Tim y explora su canal para obtener contenido valioso sobre C# y otros temas de programación. Se trata de un recurso de visita obligada para cualquiera que desee mejorar sus conocimientos de desarrollo
