2010-05-01 12 views
6

Cada vez que traté de buscar diferencias entre clases y estructuras en C# o .net, terminé con la descripción conceptual de dos cosas, como el tipo de valor o el tipo de referencia, donde se asignan las variables, etc. Pero necesito algunas prácticas diferencias He encontrado algunos comportamientos diferentes de operador de asignación, constructores, etc. ¿Alguien puede proporcionar algunas diferencias más prácticas que serán directamente útiles durante la codificación? Al igual que las cosas funciona con uno pero no con otra o la misma operación que muestra un comportamiento diferente. Y algunos errores comunes con respecto a estos dos.Diferencias prácticas entre clases y estructuras en .net (no conceptual)?

Además, sugiérales dónde considerar el uso de una estructura en lugar de una clase. Y donde las estructuras no deberían ser usadas.

Editar:? ¿Tengo que llamar al constructor de forma explícita o simplemente declarar será suficiente una variable de tipo struct (¿Debo hacer una nueva pregunta?)

Respuesta

10

OK, aquí hay algunas diferencias prácticas, específicas:

  • Una variable puede ser nula si se trata de una clase, pero nunca es nula si se trata de una estructura .

  • default(T) es null para una clase de, pero para un struct realidad Construye un valor (que consiste en un montón de ceros binarios).

  • A struct se pueden hacer anulable mediante el uso de Nullable<T> o T?. No se puede usar una clase para T en Nullable<T> o T?.

  • Un estructura siempre tiene un constructor público predeterminado (un constructor con parámetros cero).El programador no puede anular este constructor con una implementación personalizada, básicamente es "inamovible". Una clase permite que el programador no tenga un constructor predeterminado (o uno privado).

  • Los campos en una clase pueden tener valores predeterminados declarados en ellos. En una estructura no pueden.

  • Un clase puede heredar de otra clase, sino una estructura no puede ser declarada derivar de cualquier cosa (que deriva implícitamente de System.ValueType).

  • Tiene sentido utilizar una clase deen object.ReferenceEquals(), pero utilizando una variable deestructura siempre producirá falsa.

  • tiene sentido utilizar una claseen un comunicado lock(), pero utilizando una variable deestructura causará muy sutil fracaso. El código no estará bloqueado.

  • En un sistema de 32 bits, puede asignar teóricamente una serie de hasta 536,870,912 referencias a una clase, pero para una estructuraque tenga que tomar el tamaño de la estructura en cuenta porque están asignando instancias reales.

+1

Las estructuras realmente no tienen un constructor predeterminado público. Por el contrario, las estructuras que son parte de alguna otra entidad (por ejemplo, elementos de una matriz o campos dentro de una clase, o campos de otra estructura que cumplen los criterios anteriores) * implícitamente * entran en existencia cuando la entidad que los posee aparece sin ningún * * se llama al constructor, mientras que las instancias de clase solo aparecen cuando se instancian explícitamente, lo que, sin Reflection, solo puede ocurrir al invocar un constructor o mediante un método de clase que invoca 'MemberwiseClone'. – supercat

+0

Las estructuras tienen un constructor predeterminado público en todos los sentidos de la palabra: puede invocarlo, puede descubrirlo a través de Reflection, y satisface la restricción 'new()' del tipo genérico. El hecho de que haya formas de construir una estructura sin llamar explícitamente a un constructor de este tipo no la hace desaparecer repentinamente :) – Timwi

+0

La reflexión con estructuras es extraña. Las entidades reales de tipos de estructuras "reales" están fuera del sistema de tipo .net, pero cada tipo de estructura tiene un tipo de clase correspondiente que deriva de 'ValueType'; .net proporciona un operador de conversión cada vez más amplio de cada tipo de estructura a 'Object/ValueType' (que lo convierte en un objeto de clase) y un molde de reducción del objeto de clase a la estructura" real ". Cuando uno usa Reflection para crear una nueva instancia de una estructura, lo que será createde es una derivada ValueType; si uno lo lanza y lo asigna a una estructura, se convertirá en ese tipo de estructura. – supercat

-1

Aquí hay un enlace interesante: http://www.jaggersoft.com/pubs/StructsVsClasses.htm

Para en su mayor parte, no hay muchas razones convincentes para utilizar las estructuras cuando las clases ofrecen mucho más al desarrollador.

+0

Buen material de referencia. – Amry

+0

-1: Si bien la referencia puede ser buena, algunas explicaciones y ejemplos de código serían preferibles. Si ese enlace desaparece, esta respuesta no tiene sentido. – IAbstract

+0

-1 para la declaración general que hace que parezca que las estructuras son completamente inútiles y nunca deben usarse. – Timwi

2

Las estructuras en un contenedor sólo puede modificarse si el recipiente es un conjunto integrado:

struct Point { public int x, y; void Move(int dx, int dy) { x += dx; y += dy; } } 
... 
Point[] points = getPointsArray(); 
points[0].Move(10, 0) = 10; 
// points[0].x is now 10 higher. 
List<Point> points = getPointsList(); 
points[0].Move(10, 0); 
// No error, but points[0].x hasn't changed. 

Por esta razón, estoy firmemente a favor estructuras inmutables:

Point Move(int dx, int dy) { return new Point(x + dx, y + dy); } 
... 
points[0] = points[0].Move(10, 0); // Always works. 

observación general: clases usualmente son mejores Las estructuras se destacan cuando se quiere mantener estructuras de datos pequeños, conceptualmente atómicas como Point, Complejo (número), Rational, etc.

+0

Esto es cierto, pero la razón de esto puede ser difícil de percibir a primera vista, y en realidad no está relacionada con las matrices. Como las estructuras son tipos de valores, al indexar la lista se devuelve una copia de la estructura real y al cambiarla (usando el método 'Move') no cambia el valor original. El mismo comportamiento se percibiría si esa estructura estuviera en una propiedad pública de una clase diferente. Además, 'points [0] = points [0] .Move (10, 0);' también "funcionaría" con una estructura mutable. – Groo

+0

@Groo: Está relacionado con matrices en el sentido de que solo las matrices admiten la mutación de elementos de estructura. En cierto sentido, están "privilegiados", lo cual es algo que no me gusta en el diseño de un lenguaje (C++, para todas sus verrugas y escaras, no muestra este defecto con sus contenedores). Ciertamente, la semántica 'Move 'alternativa funcionaría con una estructura mutable, pero una estructura mutable típicamente no implementaría' Move' de esa manera. –

+0

@MarceloCantos: Es una lástima que .net y sus idiomas no proporcionen un medio para exponer propiedades como "call-through-reference", tal que una declaración como "someCollection [5] .X + = 1" se traduciría a algo así como: 'someCollection.ActOnItem (5, (ref item) => {item.X + = 5})'. Permitir que dicha propiedad sea pasada por 'ref' en todos los casos requeriría genéricos variados, pero incluso si el soporte se limitara a casos con no más de tres parámetros' ref' tales transformaciones aún serían útiles. Una gran ventaja sobre la posibilidad de exponer propiedades de esa manera es que el código que ... – supercat

0

Donde están asignados (pila vs. pila) no es algo que realmente le importe mientras los usa (no debe ignorar esto - debe por supuesto, estudiar las diferencias y entenderlas).

Pero la diferencia práctica más importante que llegará a través de la primera vez que se decide sustituir a su clase con una estructura, estructuras es que se pasan por valor , mientras que las instancias de clase se pasan por referencia .

Esto significa que cuando pasa una estructura a un método, se crea una copia de sus propiedades (una copia superficial) y su método obtiene una copia diferente a la que tenía fuera del método. Cuando pasa una instancia de una clase, solo se pasa al método una referencia al mismo lugar en la memoria, y su método trata exactamente con los mismos datos.

Por ejemplo, si tiene una estructura que se llama MyStruct, y una clase llamada MyClass, y los pases a este método:

void DoSomething(MyStruct str, MyClass cls) 
{ 
     // this will change the copy of str, but changes 
     // will not be made to the outside struct 
     str.Something = str.Something + 1; 

     // this will change the actual class outside 
     // the method, because cls points to the 
     // same instance in memory 
     cls.Something = cls.Something + 1; 
} 

cuando el procedimiento termina, se incrementará su clase propiedad, pero la propiedad de su estructura permanecerá sin cambios, porque la variable str dentro del método DoSomething no hace punto en el mismo lugar en la memoria.

0

La singularmente importante diferencia práctica es que las estructuras son tipos de valor, mientras que las clases son los tipos de referencia. Eso tiene algunas implicaciones.

En primer lugar, las estructuras son copiados en la asignación. Estos dos bloques de código tendrán un resultado diferente (tenga en cuenta, normalmente se debe ni utiliza campos públicos ni estructuras mutables, que estoy haciendo esto sólo para fines de demostración):

struct X 
{ 
    public int ID; 
    public string Name; 
} 

X x1 = new X { ID = 1, Name = "Foo" }; 
X x2 = x1; 
x2.Name = "Bar"; 
Console.WriteLine(x1.Name); // Will print "Foo" 

class Y 
{ 
    public int ID; 
    public string Name; 
} 
Y y1 = new Y { ID = 2, Name = "Bar" }; 
Y y2 = y1; 
y2.Name = "Baz"; 
Console.WriteLine(y1.Name); // Will print "Baz" 

X y Y son exactamente los mismos, excepto que X es un struct. Los resultados de esto son diferentes porque cada vez que asignamos un X, se hace una copia, y si cambiamos la copia, entonces no estamos cambiando el original. Por otro lado, cuando asignamos los contenidos de y1 a y2, todo lo que hemos hecho es copiar una referencia; ambos y1 y y2 se refieren físicamente al mismo objeto en la memoria.

La segunda consecuencia de estructuras siendo los tipos de valor es limitaciones genéricas. Si desea pasar de los tipos de valor, el nombre de la restricción es, literalmente, "estructura" o "clase":

public class MyGeneric<T> 
    where T : struct 
{ ... } 

Lo anterior le permitirá crear un MyGeneric<int> o MyGeneric<X>, pero no un MyGeneric<Y>. Por otro lado, si lo cambiamos a where T : struct, que estamos ya no se permite crear cualquiera de los dos primeros, pero MyGeneric<Y> está bien.

Por último, pero no menos importante, es necesario utilizar al escribir estructuras de interoperabilidad, ya que con estructuras usted es capaz de garantizar una disposición específica en la memoria.

1

A veces usted no quiere lo que está pasando a ser mutable, y desde una estructura mutable sólo puede ser pura maldad, me gustaría aléjate de volver a crear una :) He aquí un ejemplo de una situación:

class Versión:

class AccountInfo { 
    public string OwnerName { get; set; } 
    public string AccountNumber { get; set; } 
} 

struct Versión:

struct AccountInfo { 
    public string OwnerName; 
    public string AccountNumber; 
} 

ahora Imagen que llamó un método como este:

public bool TransferMoney(AccountInfo from, AccountInfo to, decimal amount) 
{ 
    if(!IsAuthorized(from)) return false; 
    //Transfer money 
} 

Un struct es una Value type, lo que significa una copia se pasa al método. La versión de la clase significa una referencia se pasa al método, que no le gustaría, por ejemplo, el número de cuenta para ser cambiables después de la autorización aprobada, usted quiere nada que cambiar en una operación como ésta ... desea una inmutable tipo de valor. There's another question here asking why mutable structs are evil ... cualquier operación en la que no desee que algo afectado por el objeto de referencia cambie, sería un lugar práctico donde un struct puede caber mejor.

El ejemplo anterior puede ser un poco tonto, pero el punto es cualquier operación sensible donde los datos pasados ​​no deberían cambiar en otro hilo o de ninguna manera sería un lugar al que se mire pasando por valor.

+0

Las propiedades no funcionan dentro de struct? – Gulshan

+0

@Gulshan - Sí, sí funcionan, solo viejo hábito escribiéndolos como arriba. Prefiero una "estructura" para mí inmediatamente reconocible por dentro y por fuera al codificar. –

0

El enlace proporcionado por Tejs (http://www.jaggersoft.com/pubs/StructsVsClasses.htm) es una buena explicación (aunque está un poco desactualizado, particularmente en la explicación de los eventos).

La diferencia práctica más importante es que una estructura es un tipo de valor, lo que significa que se pasa por el valor en lugar de hacerlo por referencia. Lo que esto realmente significa es que cuando una estructura se pasa como argumento, en realidad se pasa por copia. Como resultado, las operaciones en una instancia de una estructura no afectan otras instancias.

Considere el siguiente código:

struct NumberStruct 
{ 
    public int Value; 
} 

class NumberClass 
{ 
    public int Value = 0; 
} 

class Test 
{ 
    static void Main() 
    { 
     NumberStruct ns1 = new NumberStruct(); 
     NumberStruct ns2 = ns1; 
     ns2.Value = 42; 

     NumberClass nc1 = new NumberClass(); 
     NumberClass nc2 = nc1; 
     nc2.Value = 42; 

     Console.WriteLine("Struct: {0}, {1}", ns1.Value, ns2.Value); 
     Console.WriteLine("Class: {0}, {1}", nc1.Value, nc2.Value); 
    } 
} 

Debido a que tanto ns1 y ns2 son del tipo NumberStruct valor, cada uno tiene su propio lugar de almacenamiento, por lo que la asignación de ns2.Number no afecta al valor de ns1.Number. Sin embargo, dado que nc1 y nc2 son ambos tipos de referencia, la asignación de nc2.Number afecta el valor de nc1.Number porque ambos contienen la misma referencia.

[NB El código anterior y texto tomado de Sams Teach Yourself Visual C# 2010 in 24 Hours]

Además, como ya se ha señalado, estructuras deben ser siempre inmutable. (Sí, en este ejemplo, la estructura es mutable pero fue para ilustrar el punto.) Parte de eso significa que las estructuras no deben contener campos públicos.

Dado que las estructuras son tipos de valores, no se puede heredar de una estructura. Tampoco puede derivar una estructura de una clase base. (Sin embargo, una estructura puede implementar interfaces.)

Una estructura tampoco puede tener un contstructor público predeterminado declarado explícitamente (sin parámetros). Cualquier constructor adicional que declare debe inicializar completamente todos los campos de estructura. Structs tampoco puede tener un destructor declarado explícitamente.

Dado que las estructuras son tipos de valores, no deben implementar IDisposable y no deben contener código no administrado.

Cuestiones relacionadas