2010-07-13 10 views
30

La pregunta es simple y se le pregunta en el título.¿Las interfaces derivan de System.Object? La especificación de C# dice que sí, Eric dice que no, la realidad dice que no

C# 4.0 Especificación dice: (§4.2.2)

El tipo de clase de objeto es la última clase de base de todos los demás tipos. Cada tipo en C# directa o indirectamente deriva del tipo de clase de objeto.

Eric Lippert says: tipos

interfaz, no siendo las clases, no se derivan de objeto.

realidad dice:

Type t = typeof(ICloneable).BaseType; 
Console.WriteLine(t == null); 

verdadera

Así es spec mal o qué? ¿A quién creer?

+1

"Todo tipo en C# ...", bueno, sé que los punteros no derivan directamente de System.Object; Entonces, ¿este es un ejemplo del caso de "deriva indirectamente", o los punteros no son un tipo? – codekaizen

+0

@codekaizen Los punteros son un tema independiente. Son alienígenas del mundo .net, por lo que no se ajustan a los principales conceptos de OO. – Andrey

+0

@codekaizen from Eric "Los tipos de punteros inseguros están explícitamente fuera de las reglas de tipo normal para el idioma". – Andrey

Respuesta

30

¡No es tan simple una pregunta como se podría pensar :)

Las interfaces no hacen derivan de object pero se puede llamar a los miembros de object en ellos. Entonces puede llamar al ToString() en una expresión que tiene un tipo de tiempo de compilación de IDisposable, por ejemplo.

Casualmente, por encima de una conversación entre Neal Gafter y Eric en NDC discutir exactamente este punto ...

creo que la sección 4.2.2 de la especificación es más simplificado, por desgracia. Es de esperar que Mads y Eric lo arreglen para una versión futura. Los enviaré por correo para asegurarme de que vean esta pregunta.

También estoy luchando para encontrar algo en la especificación para respaldar el resto de esta respuesta. La sección 3.4.5 de la especificación C# 4 es lo más cercana que puedo encontrar:

Los miembros de una interfaz son los miembros declarados en la interfaz y en todas las interfaces base de la interfaz. Los miembros de la clase object no son, estrictamente hablando, miembros de ninguna interfaz (13.2). Sin embargo, los miembros de la clase object están disponibles a través de la búsqueda de miembros en cualquier tipo de interfaz (7.4).

La conversión de un tipo de interfaz a object está cubierta por la sección 6.1.6:

Las conversiones referencia implícita son:

  • Desde cualquier referencia de tipo-object y dynamic.
+0

Cualquier cosa que implemente una interfaz (clases o estructuras) va a derivar de 'object'. No estoy al tanto de los otros tipos de no objetos (por ejemplo, los punteros) que pueden implementar interfaces. –

+0

¿Diría que la especificación es correcta porque una interfaz * indirectamente * hereda? Bueno, no hereda en el sentido técnico, pero el resultado final parece comportarse como tal. –

+0

No puedo ver la diferencia entre * derivar * y * tener un tipo base efectivo *. Incluso si la declaración de clase y la interfaz son muy similares, son esencialmente diferentes, la interfaz es solo un contrato. Entonces, como en .net ninguna clase puede derivar de algo diferente a Object, cada referencia de interfaz puede convertirse a objeto. pero esto no significa para mí que realmente se derivan de un objeto. – Andrey

27

Jon está (como siempre) en el lugar. ¡No es tan fácil como crees!

La especificación es vaga y ligeramente contradictoria. En este caso particular, es probable que sea mejor entrecerrar los ojos un poco y obtener la esencia de lo que la especificación significa para transportar en lugar de analizarlo de forma estricta para obtener definiciones precisas.

El hecho simple de la cuestión es que "herencia" es un término muy usado en exceso en la programación orientada a objetos. (Me parece recordar que C++ tiene seis tipos diferentes de herencia, aunque sería difícil nombrarlos todos a corto plazo.)

Si tuviera mi druthers entonces la especificación C# indicaría claramente una diferencia entre herencia y implementación de interfaz. La herencia es * una técnica de código compartido para tipos de clase (y delegado) y struct (y enum); su mecanismo es , todos los miembros heredables de un tipo base se convierten en miembros de un tipo derivado. Eso está en contraste con la interfaz . aplicación que es el requisito de que un tipo de aplicación tengan un cierto conjunto de miembros públicos Esas dos cosas parece conceptualmente muy diferente para mí, uno es sobre miembros existentes que comparten y el otro es de unos que requieren ciertos miembros

.

Sin embargo, la especificación no lo hace, sino que los combina bajo la rúbrica de herencia. Dado que estos dos cosas algo diferentes tienen el mismo nombre en la especificación, es difícil razonar con claridad y precisión sobre las diferencias entre ellos.

yo personalmente prefiero pensar que es objeto no el "tipo de base" de cualquier interfaz, y que los miembros del objeto son no heredado por la interfaz. Que pueda llamarlos en una instancia de una interfaz es más como una cortesía que le extiende el compilador para que no tenga que insertar un molde para objetar allí.

+0

C++ tiene la misma cantidad de tipos de herencia que C# - dos. – Puppy

+7

@DeadMG: C++ tiene herencia pública, privada y protegida, que C# no tiene: C# solo tiene herencia pública en las clases. C++ tiene "herencia virtual", que C# no tiene. C++ tiene herencia múltiple, que C# no tiene. –

2

Los tipos de interfaz no heredan de Object, pero las ubicaciones de almacenamiento de los tipos de interfaz contienen referencias a objetos de clase que, si no son nulos, se heredan de System.Object.

Creo que entender lo que está pasando será más fácil si se comienza examinando la diferencia entre los tipos de valores y los tipos de clase. Supongamos que tengo una estructura:

public struct SimplePoint {public int x,y;} 

y tengo dos métodos

public doSomethingWithPoint(SimplePoint pt) ... 
public doSomethingWithObject(Object it) ... 

y Cal cada método:

SimplePoint myPoint = ...; 
doSomethingWithPoint(myPoint); 
dosomethingWithObject(myPoint); 

La primera llamada no pasa una cosa que se deriva de Object. En su lugar, pasa los contenidos de todos los campos públicos y privados de SimplePoint.La segunda llamada necesita algo que se deriva de Object, por lo que genera una nueva instancia de objeto de montón del tipo SimplePoint que contiene todos los campos públicos y privados del tipo de valor SimplePoint, y carga todos esos campos con los valores correspondientes de myPoint y pasa una referencia a ese objeto.

Tenga en cuenta que el tipo SimplePoint realmente describe dos tipos diferentes de cosas: una colección de campos (es decir, el tipo de valor) y un tipo de objeto de montón. Qué significado es aplicable depende del contexto donde se usa el tipo.

Los tipos de interfaz tienen una arruga similar: cuando se utilizan como tipos de ubicación de almacenamiento, especifican que la ubicación de almacenamiento contendrá una referencia de objeto. Cuando se usan como restricción genérica, no dicen nada sobre cómo se almacenará el tipo. Por lo tanto, una ubicación de almacenamiento de un tipo de interfaz mantendrá una referencia a un objeto de montón que realmente hereda de System.Object, pero una variable de un tipo restringido a una interfaz puede contener una referencia o un grupo de campos.

+0

La primera llamada * does * pasa una cosa que se deriva de 'object': por ejemplo, puede llamar' ToString() 'en ella. – svick

+0

@svick: Solo es posible llamar a 'ToString' directamente en los tipos de valores que definen su propia implementación de ese método; si uno escribe '3.ToString();', no se realizará como una llamada virtual a 'Object.ToString(),' sino que simplemente se realizará como una llamada directa al método 'ToString()' que es definido para 'Int32'; tal llamada está permitida no porque 'Int32' se deriva de' Object', sino porque 'Int32' define su propio método' ToString() '. Invocar 'ToString' en un tipo de valor que no define su propia versión hará que el sistema cree una nueva instancia de objeto de montón que ... – supercat

+0

... contiene datos copiados del tipo de valor (y que deriva de' Objeto'), y luego llamará a 'ToString' sobre eso. Lo hace porque el tipo de valor unboxed es convertible a object. Algunas personas intentan pretender que las estructuras en caja y sin caja son del mismo tipo. Es cierto que están descritos por la misma instancia de 'Tipo', pero dado su diferente comportamiento, creo que es más útil considerarlos como tipos diferentes. – supercat

Cuestiones relacionadas