2010-09-08 4 views
7

Pregunto esto porque parece que usar Object parece ser una forma fácil de resolver ciertos problemas, como "No tengo un tipo específico, así que use Object", etc.¿Podría .NET funcionar igual de bien sin el uso de Type Object?

También el motivo Me dio curiosidad porque un colega mío me dijo que si .NET era una verdadera plataforma orientada a objetos, entonces no tendría que tener un tipo de captura como Object.

Entonces, si .NET no tiene el tipo de objeto, ¿cuáles serían las formas alternativas de resolver los problemas que ocurren para que funcione de la misma manera?

También solo para tener en cuenta, esto no es para bash .NET, ya que lo uso a diario en mi trabajo. Solo quiero saber más sobre eso.

EDITAR: Otra nota que recordé es porque existe el tipo Objeto, su efecto se extiende a lo largo de todo el .NET. Como IEnumerable existe pero también IEnumerable <T>. Y en muchas situaciones tiene que implementar tanto la versión genérica como la no genérica de las cosas, etc.

+2

¿No tiene que haber un objeto base para todo en última instancia para derivar? – ChrisF

+0

quizás podría dar un ejemplo de problemas que el tipo de objeto puede resolver. –

+3

@ChrisF: No necesariamente; ¿De qué se deriva 'Object'? –

Respuesta

2

En un marco fuertemente tipado, los objetos tienen que comenzar en alguna parte.

El tipo object no está ahí para proporcionar una opción de fundido "fácil" o para forzarlo a reducir al mínimo común denominador. Está ahí para proporcionar el caso más general absoluto para un objeto: una base para la comparación de referencia y un práctico método ToString(), entre algunos otros.

+2

Y el método GetType(), que se hereda de System.Object porque sabemos que todos los objetos tienen un tipo. – FacticiusVir

+0

Gracias, ¿quiere decir que Object ayuda porque puede proporcionar el método ToString para todo? ¿No podría el compilador hacer esto detrás de las cubiertas para que parezca que todo tipo lo tiene? –

+0

@Joan: me gusta el hecho de que System.Object implemente explícitamente los conceptos básicos, incluidos GetType() (como se menciona FacticiusVir) y ToString(). No me gustaría que el compilador trabaje con un vudú místico bajo las sábanas que no podría ver sin Reflector. –

3

Yo diría que el problema que se soluciona con Object no es "No tengo un tipo específico, así que use Object", sino "Realmente no me importa de qué tipo es esto; todo lo que necesito saben que es un Object "

3

es una ventaja, no sólo para su uso como un tipo 'genérico', sino también para la reflexión, la recolección de basura, etc.

Otros lenguajes (C++) pueden prescindir, pero Yo dudaría en decir que eso hace que esos idiomas sean más POO.

+0

Bueno, yo diría que hay un equivalente a 'Objeto' en C++:' void * ' –

+0

@Willy: No, nadie hereda de' void * '. Solución diferente a un problema diferente (puntero). –

+2

La pregunta del OP no mencionó específicamente la herencia, ni respondió esto. Quizás no debería haber dicho "equivalente", pero creo que en muchas situaciones el concepto 'void *' de C++ resuelve los mismos problemas que el tipo de datos .NET 'Object'. Si quisiera construir algún tipo de clase de contenedor en C++, pero no sabía exactamente qué tipo de objetos pondría en mi clase contenedor, podría usar 'void *'. Alternativamente, también podría usar plantillas de C++, pero el único equivalente de .NET es Generics, que solo se introdujo en la versión 2.0. –

1

Sí, el tipo de objeto puede ser mal utilizado, pero proporciona una funcionalidad fundamental para el mundo de .NET (sobre todo IMO es GetType()). Entonces, si hay un problema, no es tener el tipo de objeto.

Las alternativas son muchas, incluyendo los genéricos y las prácticas de programación orientada a objetos, tales como interfaces ...

2

Recuerde que denota una herencia "es-un" relación. Cada clase en .NET "es un (n)" objeto. Todos tienen un método ToString. Todos ellos tienen un tipo al que se accede a través del GetType. Este tipo de relación y la compartición resultante de la funcionalidad es la base de la programación orientada a objetos.

+0

Gracias, ¿quiere decir que Object ayuda porque puede proporcionar el método ToString para todo? ¿No podría el compilador hacer esto detrás de las cubiertas para que parezca que todo tipo lo tiene? –

+0

@ Joan-Venge Ese no es el punto de 'Objeto', pero estoy señalando que todas las clases comparten y deben compartir un pequeño conjunto de atributos entre sí. Estas características comunes se expresan a través del tipo 'Object'. Muchos de los usos anteriores de 'Objeto' probablemente podrían manejarse ahora con genéricos, pero esto aún no va en contra de OO. Si todas sus clases son versiones más específicas de una única clase base, ¿no debería implementar esa clase base para consolidar la funcionalidad y proporcionar un contrato para otras piezas de código? – Jake

+0

Sí, eso tiene sentido. –

1

Si la jerarquía de derivación del objeto no tiene un solo encabezado unificado, entonces sería imposible probar la igualdad entre dos cosas arbitrarias ... uh ... sin recurrir al tipado dinámico.

Aparte de eso, sospecho que la funcionalidad del objeto también podría haber sido manejada por interfaces separadas (IEable e IConvertableToString). Por otro lado, los métodos virtuales de los objetos son bastante útiles a veces, especialmente ToString, que puede ser utilizado por un IDE o un depurador al mostrar el estado del programa. Es realmente un diseño pragmático.

+0

Gracias, estaba pensando en IEable e IConvertableToString también. –

3

Tu amigo probablemente trabaje con un lenguaje dinámico (como ruby ​​o python), ¿verdad?

Hay una gran cantidad de personas que piensan que los lenguajes fuertemente tipados deben denominarse "orientados a clases" en lugar de "orientados a objetos", porque todos los comportamientos posibles y polimorfismo deben hacerse por adelantado en la definición de clase. Cuando su método acepta cualquier cosa, cualquier comprobación se realiza en función de las capacidades de los objetos en lugar de su clase, se podría decir que es un enfoque más orientado a objetos.

Tengo un tipo de conflicto en este argumento. Existe esta creencia desquiciada en los círculos de programación de que OO es inequívocamente bueno, sin importar el problema o los requisitos. Por eso, las personas tienden a tratar de ganar argumentos diciendo "tal o cual cosa no está orientada a objetos", y dado que orientado a objetos es sinónimo de bueno, ganan. A pesar de que creo que los lenguajes estáticos son un dolor para trabajar, creo que llamarlos no OO es una manera poco sincera de expresar tu punto. Por otro lado, cualquier cosa que haga que un programador llegue a su zona de confort y aprenda una nueva forma de hacer algo (si no es por otra razón que ganar una discusión) no puede ser totalmente malo. Como dije, en conflicto. :)

+0

Él es en realidad un programador de C++, pero creo que tiene un sesgo/favor en Python. –

+1

Sí, probablemente es de donde está sacando eso. Como él, si lo es porque piensa que C# está orientado a las clases más que a los objetos. –

1

No estoy especialmente informado sobre este tema, pero desde mi punto de vista, fue útil permitir a las personas construir componentes polimórficos sin tener un conocimiento previo de cómo se consumirían esos componentes. ¿Qué quiero decir con eso? Déjame intentar explicar.

Tomemos un ejemplo simple con la clase ArrayList del .NET framework. Esto fue parte del marco original, antes de que se introdujeran los genéricos. Los autores de la clase ArrayList intentaban proporcionar una implementación útil de la lista dinámica, pero no tenían forma de saber qué tipos de objetos se insertarían en la lista. Utilizaron el tipo de objeto para representar los elementos en la lista porque permitiría agregar cualquier tipo de clase a la lista. Por ejemplo:

 ArrayList people = new ArrayList(); 
     people.Add(new Doctor()); 
     people.Add(new Lawyer()); 
     people.Add(new PetDetective()); 
     people.Add(new Ferrari()); // Yikes! 

     // ... 

     for (int i = 0; i < people.Count; i++) 
     { 
      object person = people[0]; 
      // ... 
     } 

Ahora bien, si este fuera su propia aplicación y que sabía que sus Doctor, Lawyer y PetDetective clases derivan de una clase Person base común, entonces se podría, en teoría, construir su propia vinculados lista implementación basada en la clase Person en lugar de la clase Object. Sin embargo, eso es mucho trabajo adicional con muy poco beneficio cuando ya tiene una clase ArrayList construida y probada. Si realmente desea hacerlo específico para su clase base Person, entonces siempre puede crear una clase contenedora para ArrayList que solo acepta Person -objetos derivados.

En C++, podría hacer prácticamente lo mismo utilizando el tipo de datos "puntero void" (void*). Sin embargo, C++ también soportaba plantillas (muy similares a los genéricos), lo que facilitaba la creación de un componente útil sin conocer los detalles de con qué otras clases se utilizaría. Como C# no era compatible inicialmente con los genéricos, usar el tipo Object era realmente la única manera de crear componentes polimórficos generales para que otras personas los usen.

+0

Entonces, ¿el motivo para tener el tipo Objeto tuvo que ser agregado simplemente porque Generics no estaba en la versión 1? Si fuera así, no necesitaríamos el tipo Object? –

+1

Realmente no sé si eso fue un motivo de conducción; Ciertamente no estoy en condiciones de decir cuáles fueron las motivaciones de Microsoft en ese momento. Sin embargo, solo estoy tratando de ilustrar una posible razón de su existencia. Como han señalado otros, también proporciona una implementación básica para Equals, GetHashCode y ToString, pero estoy de acuerdo con su punto de que un enfoque alternativo podría haber sido tener una interfaz especial que defina esos métodos y tener el compilador implícitamente. hacer que todas las clases implementen esa interfaz. –

1

La idea de que el concepto de System.Object podría ser reemplazado por interfaces que, por regla general, todas las clases implementan ha sido mencionado algunas veces en esta pregunta. Aunque creo que la idea es válida, al final diría que no te compra nada. Incluso si existieran tales interfaces, quedaría la pregunta de cómo el compilador realmente se aseguraría de que las clases implementaran esas interfaces.

A continuación se muestra un código de ejemplo en el que trato de explorar una situación hipotética donde no hay System.Object tipo y cómo se vería la implementación bajo el capó, así como el uso de esas interfaces.

// let's start off by defining interfaces to describe the various methods that are currently available from the System.Object class 

public interface IEquatable 
{ 
    bool Equals(IEquatable other); 
} 

public interface IHashCodeGenerator 
{ 
    int GetHashCode(); 
} 

public interface ITypeIdentifiable 
{ 
    Type GetType(); 
} 

public interface IConvertibleToString 
{ 
    string ToString(); 
} 

// This guy throws a wrench into things, because we can't privately (or "protectedly") implement an interface. 
// This is discussed further below on the MyClass.MemberwiseClone method. 
public interface IMemberwiseCloneable 
{ 
} 

// This class simply encapsulates similar functionality found within the System.Object class 
public static class ClrInternals 
{ 
    [MethodImpl(MethodImplOptions.InternalCall)] 
    internal static extern bool Equals(IEquatable objA, IEquatable objB); 

    [MethodImpl(MethodImplOptions.InternalCall)] 
    internal static extern int GetHashCode(IHashCodeGenerator hashGenerator); 

    [MethodImpl(MethodImplOptions.InternalCall)] 
    internal static extern Type GetType(ITypeIdentifiable typedInstance); 

    [MethodImpl(MethodImplOptions.InternalCall)] 
    internal static extern IMemberwiseCloneable MemberwiseClone(IMemberwiseCloneable original); 
} 

// let's say that as a rule the compiler implicitly makes all classes implement these interfaces 
class MyClassExampleA : IEquatable, IHashCodeGenerator, ITypeIdentifiable, IConvertibleToString, IMemberwiseCloneable 
{ 
    // The compiler also implicitly makes all classes implement the interfaces with the following code (unless otherwise specified) 

    #region IEquatable Members 

    public bool Equals(IEquatable other) 
    { 
     // let's suppose that this is equivalent to the current implementation of Object.Equals 
     return ClrInternals.Equals(this, other); 
    } 

    #endregion 

    #region IHashCodeGenerator Members 

    public int GetHashCode() 
    { 
     // let's suppose that this is equivalent to the current implementation of Object.GetHashCode 
     return ClrInternals.GetHashCode(this); 
    } 

    #endregion 

    #region ITypeIdentifiable Members 

    public Type GetType() 
    { 
     // let's suppose that this is equivalent to the current implementation of Object.GetType 
     return ClrInternals.GetType(this); 
    } 

    #endregion 

    #region IConvertibleToString Members 

    public string ToString() 
    { 
     // let's suppose that this is equivalent to the current implementation of Object.ToString 
     return this.GetType().ToString(); 
    } 

    #endregion 

    // this one is perhaps a little goofy, since it doesn't satisfy any interface 
    // In order to be equivalent to the current Object.MemberwiseClone implementation, I've made this protected, 
    // but we cannot have a protected method that implements an interface, so this throws a wrench into things. 
    protected MyClassExampleA MemberwiseClone() 
    { 
     // let's suppose that this is equivalent ot the current implementation of Object.MemberwiseClone 
     return (MyClassExampleA)ClrInternals.MemberwiseClone(this); 
    } 

    // ** All of the above code is just a representation of the implicit semantics that the compiler/CLR applies to a class. Perhaps this code is not actually generated by the compiler for each class (that would be a lot of duplication!), but rather the CLR might handle this logic internally 
} 


// Ok, so now I'm implementing a general Stack class 
public class Stack 
{ 
    // what type should I use for the parameter? 
    // I have five different interfaces to choose from that I know all classes implement, but which one should I pick? 
    public void Push(type??? item) 
    { 
     // ... 
    } 

    // what type should I use for the return type? 
    // I have five interfaces to choose from, but if I return one, 
    // then my caller can't utilize the methods defined in the other interfaces without casting. 
    // I know all classes implement all five interfaces, but is it possible that my Stack might also contain non-class objects that don't implement all interfaces? In that case it might be dangerous for the caller to cast the return value from one interface to another. 
    public type??? Pop() 
    { 
     // ... 
    } 

    // In C++ I could have used void* or defined the Stack class as a template 
} 

// moving on... 
class StackUtilizer 
{ 
    // here I try to utilize the Stack class 
    public void UseStack(Stack stack) 
    { 
     // what type should I use for the variable to hold the result of the Stack.Pop method? 
     type??? item = stack.Pop(); 

     // if I use IEquatable 
     IEquatable item1 = stack.Pop(); 

     IEquatable item2 = stack.Pop(); 

     item1.Equals(item2); // then I can do this 

     Type itemType = item1.GetType(); // but I can't do this 

     string s = item1.ToString(); // nor can I do this 

     // Ok, this calls for another interface that composes all of these other interfaces into one 
    } 
} 


// let's define a single interface that pulls all of these other interfaces together 
public interface IObject : IEquatable, IHashCodeGenerator, ITypeIdentifiable, IConvertibleToString, IMemberwiseCloneable 
{ 
    // no need to define any methods on this interface. The purpose of this interface is merely to consolidate all of these other basic interfaces together. 
} 

// now we change the compiler rule to say that all classes implicitly implement the IObject interface 
class MyClassExampleB : IObject 
{ 
    // ... <refer to MyClassExampleA for the implicit implementation of the interfaces> 
} 

// now let's try implementing that Stack class again 
public class Stack 
{ 
    // I know that all classes implement the IObject interface, so it is an acceptable type to use as a parameter 
    public void Push(IObject item) 
    { 
     // ... 
    } 

    // again, since all classes implement IObject, I can use it as the return type 
    public IObject Pop() 
    { 
     // ... 
     throw new NotImplementedException("This is an example. The implementation of this method is irrelevant."); 
    } 
} 

class StackUtilizer 
{ 
    // here I try to utilize the Stack class 
    public void UseStack(Stack stack) 
    { 
     // now I can just use IObject for my variables holding the return value of the Stack.Pop method 
     IObject item = stack.Pop(); 

     // if I use IObject 
     IObject item1 = stack.Pop(); 

     IObject item2 = stack.Pop(); 

     item1.Equals(item2); // then I can do this 

     Type itemType = item1.GetType(); // and I can do this 

     string s = item1.ToString(); // and I can do this 
    } 
} 

De modo que, al final, todavía tenemos una interfaz IObject, similar a la clase System.Object actual. La pregunta abierta es cómo se encargaría el compilador/CLR de hacer cumplir nuestra regla de que todas las clases implementan la interfaz IObject. puedo pensar en tres enfoques posibles:

  1. El compilador genera la implementación de la interfaz implícita para cada clase, lo que causaría una gran cantidad de duplicación.
  2. El CLR manejaría esas implementaciones de interfaz de una manera especial que no requeriría que el compilador realmente generara código para cada clase.
  3. Definimos una clase base, llamémosla Object (empezando a sonar familiar?), Que implementa la interfaz IObject y cambiamos la regla para decir que todas las clases heredan implícitamente de Object (esto es exactamente lo que tenemos hoy, pero sin las interfaces).
+0

'class Stack {public void Push (elemento apilable) {...} ...} interfaz IStackable {} class IntStackItem: IStackable {public int value; } // Ta-da! 'Sí, eso es bastante terrible. No importa. –

+0

Ja ja. De hecho, ese es un enfoque bastante ingenioso, dada la situación. ¡Gracias! –

+0

Pensando en eso un poco más, esa sería una buena razón para permitir que los consumidores declaren que una clase/estructura/etc. implementa una interfaz, incluso si no tienen control sobre la declaración. Lo que quiero decir es que actualmente tienes que tener control sobre la declaración de una clase para decir que implementa una interfaz. Sería útil poder decir que una clase implementa una interfaz (si cumple los requisitos de esa interfaz), incluso si el autor original de esa clase no conocía esa interfaz. –

Cuestiones relacionadas