¿Qué es el corte de objetos y por qué no sucede en C#?

Si tenemos dos clases, clase A y clase B , y B se deriva de A , entonces cuando asignamos un objeto de clase B a un objeto de clase A , todos los atributos adicionales del objeto de clase B se cortan para formar el objeto de clase base ( objeto de clase A ). En palabras simples, cuando los componentes adicionales de una clase derivada se dividen o no se utilizan y la prioridad se da al objeto de la clase base, esto se denomina división de objetos. 

Ahora te estarás preguntando por qué tenemos que priorizar los objetos cuando todo está almacenado en la clase, pero aquí es donde muchos codificadores se equivocan; el valor de la memoria se almacena en el objeto de una clase y nunca en la clase en sí. Hipotéticamente, si solo tenemos 20 KB de espacio donde tenemos que agregar los objetos de la Clase A y la Clase B y el objeto de la Clase A consume un espacio de 20 KB, entonces el objeto de la Clase B se dividirá automáticamente. 

Ejemplo del lenguaje C++:

C++

// C++ program to demonstrate where the
// object slicing can occur
#include <iostream>
using namespace std;
 
// Base class
class A {
public:
    void fun1() { cout << "Function 1" << endl; }
};
 
// Derived Class
class B : public A {
public:
    void fun2() { cout << "Function 2" << endl; }
};
 
// Slicing of object is taking place in this function
void test(A slicedObject) { slicedObject.fun1(); }
 
int main()
{
    B fullObject; // object of class B
    test(fullObject);
    return 0;
}

Producción:

Function 1

Explicación: En el código anterior creamos dos clases:

  • Clase A
  • Clase B 

Clase A : contiene una función llamada fun1 que imprime en la consola la frase «Función 1».

Clase B: contiene una función llamada fun2 que imprime en la consola la oración «Función 2», y hereda de la clase A , por lo que también puede usar fun1 .

Ahora tenemos la prueba de función que toma un parámetro de tipo A. Si intentamos pasar un objeto de tipo B a la función, tendrá éxito pero con un costo de lo que se llama segmentación de objetos. Ahora, si intentamos ejecutar fun2 dentro de la prueba de función , generará un error de compilación porque no puede encontrar la función dentro de las funciones miembro del objeto.

Motivo: una instancia de clase secundaria puede tener un tamaño en memoria mayor o igual que la clase principal porque una clase secundaria contiene todos los atributos y funciones de la clase principal y también puede tener atributos o funciones adicionales.

Por lo tanto, cuando intentemos crear una instancia de la clase principal y asignarle una instancia de la clase secundaria, no habrá memoria suficiente para que el objeto principal contenga todos los atributos y funciones adicionales de la instancia de la clase secundaria.

Por lo tanto, corta todos los atributos y funciones adicionales de la instancia de la clase secundaria para formar la instancia de la clase principal que solo tiene atributos y funciones que se encuentran en la clase principal.

Para obtener más detalles, consulte el artículo Segmentación de objetos en C++ .

Mismo ejemplo en C#:

C#

// C# program to demonstrate where the
// object slicing can occur
using System;
 
// Base class
class A {
    public void fun1() { Console.WriteLine("Function 1"); }
}
 
// Derived class
class B : A {
    public void fun2() { Console.WriteLine("Function 2"); }
}
 
 
class Program {
    public static void Main()
    {
        B fullObject = new B();
        test(fullObject);
    }
    public static void test(A notSlicedObject)
    {
        notSlicedObject.fun1();
    }
}
Producción

Function 1

En el código anterior, creamos el mismo ejemplo que hicimos anteriormente, pero esta vez con C#.

Notaremos que todavía no podemos usar notSlicedObject.fun2() , entonces, ¿cuál es la diferencia?

La diferencia es que notSlicedObject es una referencia a un objeto de tipo A , por lo que solo puede usar métodos o atributos limitados a ese tipo de objeto.

Pero podemos convertir el tipo de la referencia para que haga referencia a un objeto de tipo B en lugar de tipo A y de esa manera podemos acceder a los atributos y métodos vinculados al tipo B.

C#

// C# program to demonstrate where could the
// object slicing can occur
using System;
 
class A {
    public void fun1()
    {
        Console.WriteLine("Function 1");
    }
}
 
class B : A
{
    public void fun2()
    {
        Console.WriteLine("Function 2");
    }
}
class Program
{
    public static void Main()
    {
        B fullObject = new B();
        test(fullObject);
         
    }
    public static void test(A notSlicedObject)
    {
        ((B)notSlicedObject).fun2();
    }
    
}
Producción

Function 2

Nota: La conversión del tipo de referencia no cambió el tipo del objeto en sí, sino que el único cambio que ocurrió fue en la referencia a ese objeto. El objeto en sí no tuvo que ser cortado porque nunca cambió.

Solo cambiamos el tipo de la referencia que hace referencia a ese objeto, de modo que cuando decimos que la referencia hace referencia a un objeto de tipo A , entonces la referencia solo tiene acceso a los atributos y métodos que tendría un objeto de tipo A , y cuando decimos la referencia hace referencia a un objeto de tipo B , entonces la referencia solo tiene acceso a los atributos y métodos que tendría un objeto de tipo B.

Y tenga en cuenta que solo podemos hacer este tipo de conversión o conversión solo a clases que tienen una relación de herencia entre ellas como el ejemplo anterior, de lo contrario, debe definir explícitamente esa conversión.

Para obtener más información sobre la conversión definida por el usuario en C#, consulte esto: Operadores de conversión definidos por el usuario

¿Por qué el corte de objetos no ocurre en C#?

Una clase en C# es un tipo de referencia, lo que significa que cuando se crea el objeto, se asigna suficiente memoria en el montón administrado para ese objeto específico, y la variable contiene solo una referencia a la ubicación de dicho objeto.

Ejemplo:

C#

// Declaring an object of type MyClass.
MyClass mc = new MyClass();

Aquí el objeto se almacena en el montón mientras que una referencia a ese objeto se almacena en la variable mc .

Eso significa que cuando tratamos de asignar una instancia de clase secundaria a una instancia de clase principal, lo que realmente sucede es que asignamos un valor de referencia de una instancia de clase secundaria a una variable de referencia de un tipo de clase principal.

Y debido a que las referencias almacenan solo la dirección de memoria de ese objeto en el montón, todas las referencias tienen el mismo tamaño en la memoria (generalmente 4 bytes para la arquitectura de CPU de 32 bits y 8 bytes para la arquitectura de 64 bits), podemos asignar o convertir fácilmente el escriba la referencia sin cortar el objeto porque no almacenamos su valor directamente, sino que usamos referencias.

Entonces, cuando tratamos con objetos de clase, no nos referimos a ellos directamente, sino que nos referimos a ellos usando referencias o punteros a objetos y esa es la razón por la cual en C# el corte de objetos no ocurre a diferencia de otros lenguajes de programación como C++ en el que el objeto puede ocurrir un corte.

Publicación traducida automáticamente

Artículo escrito por bemwa_malak y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *