Equivalente de clase de amigo en C#

Abdul Mateen 12 octubre 2023
  1. Clase de amigo en C++
  2. Use el equivalente de C# de la clase Friend
Equivalente de clase de amigo en C#

Este tutorial le enseñará sobre los equivalentes de clase amiga en C#. Primero, discutiremos la clase amiga y su uso en la programación con ejemplos de C++.

A continuación, a falta de una clase amiga equivalente en C#, veremos las alternativas.

Clase de amigo en C++

Una característica de programación importante que se aplica principalmente en la programación orientada a objetos es la ocultación de datos. Mantener los miembros de datos ocultos desde fuera de la clase significa que los miembros de datos solo se pueden actualizar dentro de la clase.

Por lo tanto, podemos decir que solo una clase (que contiene miembros de datos) puede cambiar el valor de los miembros de datos.

Al ocultar datos, reducimos el alcance de la prueba porque debemos probar la clase que estamos modificando. Ninguna otra clase puede acceder a los miembros de datos privados de alguna clase; por lo tanto, no hay necesidad de probarlos si no se modifica su código.

A veces, la ocultación de datos se ve un poco comprometida en códigos largos y complejos. Una clase puede declarar algunas funciones o clases como amigos.

En respuesta, las funciones de amigo y las clases de amigo pueden acceder a los miembros de datos privados.

La sintaxis de la amistad está abajo.

class ABC {
  ... friend class XYZ;
  friend int change_data_member(int, char);
  ...

La clase ABC declara a la clase XYZ como amiga en la línea 3. La línea #4 muestra la sintaxis para declarar un método amigo.

Aquí, la clase ABC declara el método change_data_member como una función amiga.

Después de recibir la bandera de amistad de la clase ABC, todas las funciones de la clase XYZ pueden acceder a los datos privados de los miembros de la clase ABC.

Sin embargo, en caso de otorgar amistad a una función tiene un alcance limitado de amistad. Solo la función amigo (es decir, declarado como amigo) puede acceder a los datos privados de los miembros de la clase que concede la amistad.

Veamos la implementación del concepto de amistad en C++.

#include <iostream>
using namespace std;
class ABC {
  int privateMember;

 public:
  ABC(int privateMember) { this->privateMember = privateMember; }
  friend int getPrivateMember(ABC &);
  friend void setPrivateMember(ABC &, int);
};
int getPrivateMember(ABC &obj) { return obj.privateMember; }
void setPrivateMember(ABC &obj, int privateMember) {
  obj.privateMember = privateMember;
}
int main() {
  ABC abc(5);
  cout << "Private Member: " << getPrivateMember(abc) << '\n';
  setPrivateMember(abc, 7);
  cout << "Private Member: " << getPrivateMember(abc) << '\n';
  return 0;
}

En este código, la clase ABC tiene un miembro de datos privados privateMember. En las dos últimas líneas del cuerpo de la clase, se concede amistad a dos funciones que no son de clase: getPrivateMember y setPrivateMember.

La salida de este código está debajo.

Private Member: 5
Private Member: 7

Se accede al miembro de datos privados tanto para la entrada como para la salida. El siguiente es un ejemplo de una clase amigo:

#include <iostream>
using namespace std;
class ABC {
  int privateMember;
  friend class DEF;
};
class DEF {
  ABC abc;

 public:
  int getPrivateMember() { return abc.privateMember; }
  void setPrivateMember(int privateMember) {
    abc.privateMember = privateMember;
  }
};
int main() {
  DEF def;
  def.setPrivateMember(12);
  cout << "Private Member: " << def.getPrivateMember() << '\n';
  return 0;
}

En la segunda línea de la clase ABC, se concede amistad a la clase DEF. Teniendo estatus de amistad, los métodos getPrivateMember y setPrivateMember de la clase DEF pueden acceder a los miembros privados de la clase ABC.

La salida de este código está debajo.

Private Member: 12

Esta función amigo o concepto de clase amigo está disponible en C++, no en C#. Sin embargo, existen algunos métodos a través de los cuales podemos acceder a funciones de datos que no son miembros.

Use el equivalente de C# de la clase Friend

Existen diferentes métodos para acceder a miembros de datos de cualquier clase desde algunas funciones que no son miembros en C#. Discutamos esto uno por uno.

Uso de clases anidadas

El equivalente/alternativa de C# más cercano a una clase amiga de C++ es crear una clase anidada/interna. Una clase anidada puede acceder a los miembros de datos privados de una clase externa.

El siguiente ejemplo crea la clase Inner_class como una clase anidada en Outer_class. Al estar dentro del alcance de una clase contenedora, la clase_interna puede acceder a miembros privados de la clase_externa, imitando así a la clase amiga.

using System;
public class Outer_class {
  int privateMember;
  Outer_class(int privateMember) {
    this.privateMember = privateMember;
  }
  public class Inner_class {
    public void innerClassFunction() {
      Outer_class obj = new Outer_class(5);
      Console.WriteLine("Private Member: " + obj.privateMember);
    }
  }
}
public class Driver {
  static public void Main() {
    Outer_class.Inner_class obj = new Outer_class.Inner_class();
    obj.innerClassFunction();
  }
}

En el código anterior, tenemos una clase externa y una clase interna. La variable privateMember es un miembro privado de la clase externa.

Dentro de Inner_class, estamos accediendo al miembro de datos privados que es privateMember de la clase externa.

A continuación, tenemos la clase Driver, donde hemos creado un objeto de la Inner_class. Usando el objeto Inner_class, llamamos a innerClassFunction dentro de Inner_class.

Producción :

Private Member: 5

Aunque el código anterior se compila y ejecuta sin problemas, aún así, se prefiere separar la clase_interna de la clase_externa mediante el uso del concepto de clase parcial como se indica en el siguiente ejemplo.

using System;
public partial class Outer_class {
  int privateMember;
  Outer_class(int privateMember) {
    this.privateMember = privateMember;
  }
}
public partial class Outer_class {
  public class Inner_class {
    public void innerClassFunction() {
      Outer_class obj = new Outer_class(5);
      Console.WriteLine("Private Member: " + obj.privateMember);
    }
  }
}
public class Driver {
  static public void Main() {
    Outer_class.Inner_class obj = new Outer_class.Inner_class();
    obj.innerClassFunction();
  }
}

En la segunda definición de clase parcial de Outer_class, tenemos una función innerClassFunction. Este método innerClassFunction crea un objeto de la clase externa.

Pasamos 5 como argumento para el parámetro constructor al crear un objeto Outer_class. Este parámetro de constructor se convierte en el valor del miembro de datos en la primera definición parcial de la clase externa, como se ve en el resultado a continuación.

Private Member: 5

Las clases parciales son importantes en proyectos grandes donde varias personas escriben diferentes partes de una clase.

Aquí, estamos explotando esta opción de clase parcial y escribiendo Inner_class dentro como otra porción parcial de la clase externa. Este esquema parcial puede mantener las unidades parciales separadas, pero podemos usarlas como una sola unidad.

La principal ventaja de este esquema es que la definición de clase real no está expuesta a quien escribe la definición de Inner_class.

Objetos de reflexión

Usando los objetos de Reflection, podemos obtener información de tipo de tiempo de ejecución.

Podemos usar objetos Reflejo para obtener la información del Tipo durante la ejecución del programa. El espacio de nombres System.Reflection se puede utilizar para acceder a los metadatos de un programa en ejecución.

El System.Reflection proporciona información útil en tiempo de ejecución y ayuda a agregar diferentes tipos, valores y objetos de clase al programa.

Además, podemos manejar los métodos usando el espacio de nombres System.Reflection y probar los métodos privados de la clase.

De esta forma, podemos invocar métodos privados de la clase. Por lo tanto, puede replicar la clase amigo.

Por ejemplo, asuma la siguiente clase con un método privado callable_fun().

public class Class1 {
  private char callable_fun() {
    return 'C';
  }
}

Para probar la función privada callable_fun, necesitamos escribir el siguiente código:

using System.Reflection;
public class Driver {
  static public void Main() {
    Class1 c = new Class1();
    Type class1Type = c.GetType();
    MethodInfo callMeMethod =
        class1Type.GetMethod("callable_fun", BindingFlags.Instance | BindingFlags.NonPublic);
    char result = (char)callMeMethod.Invoke(c, null);
    Console.WriteLine("Output:" + result);
  }
}

La salida de este código está debajo.

Output:C

Si está utilizando Visual Studio Team System, en ese caso, puede hacer que VS genere automáticamente una clase de proxy con accesos privados haciendo clic con el botón derecho en el método y seleccionando Crear pruebas unitarias....

Uso de la clase InternalsVisibleToAttribute

Esta clase ayuda a mejorar la visibilidad de un ensamblaje específico para aquellos tipos que normalmente solo son visibles dentro del ensamblaje actual.

// Syntax
[System.AttributeUsage(System.AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)]
public sealed class InternalsVisibleToAttribute : Attribute

Si tenemos dos ensamblados sin firmar, entonces la clase InternalsVisibleToAttribute ayuda a acceder al miembro privado de un ensamblado sin firmar por otro ensamblado sin firmar. El nombre de un ensamblado (que tiene el nombre de la clase/ensamblado amigo) se pasa como argumento.

A continuación, veamos un ejemplo en el que se usa InternalsVisibleToAttribute para acceder al miembro privado de otro ensamblado sin firmar. Aquí, SecondClass accede al miembro privado de FirstClass.

Vea el siguiente código.

using System;
using System.Runtime.CompilerServices;
using Utilities.Example;

[assembly:InternalsVisibleToAttribute("FirstClass")]

namespace Utilities.Example {
  public class FirstClass {
    internal static int InnerMethod(int x) {
      return x * x;
    }
  }
}

El siguiente ejemplo proporciona el código fuente para el ensamblado SecondClass:

public class SecondClass {
  public static void Main() {
    Console.WriteLine(FirstClass.InnerMethod(10));
  }
}

La salida de este código es 100.

Si tenemos ensamblados firmados, no se requiere la ruta del directorio ni la extensión del nombre del archivo.

El siguiente ejemplo usa InternalsVisibleToAttribute para llamar al método privado llamado innerMethod en un ensamblado firmado visible para otro ensamblado firmado. Define la clase FirstClass que incluye un método interno innerMethod.

using System;
using System.IO;
using System.Runtime.CompilerServices;

[assembly:InternalsVisibleTo(
    "Friend_Assembly, PublicKey=002400000480000094" + "0000000602000000240000525341310004000" +
    "001000100bf8c25fcd44838d87e245ab35bf7" + "3ba2615707feea295709559b3de903fb95a93" +
    "3d2729967c3184a97d7b84c7547cd87e435b5" + "6bdf8621bcb62b59c00c88bd83aa62c4fcdd4" +
    "712da72eec2533dc00f8529c3a0bbb4103282" + "f0d894d5f34e9f0103c473dce9f4b457a5dee" +
    "fd8f920d8681ed6dfcb0a81e96bd9b176525a" + "26e0b3")]

public class FirstClass {
  internal static int innerMethod(int x) {
    return x * x;
  }
}

Supongamos que el siguiente ejemplo se compila en un ensamblado con nombre seguro denominado Asamblea_Amigo. El método interno en FirstClass llamará con éxito al innerMethod, aunque el método es interno a FirstClass.

public class SecondClass {
  public static void Main() {
    int result = FirstClass.innerMethod(10);
    Console.WriteLine(result);
  }
}

La salida de este código es, de nuevo, 100.

Hemos presentado diferentes equivalentes de la clase friend de C++ en C#. Los lectores pueden usar cualquiera según sus requisitos.

Artículo relacionado - Csharp Class