2008-11-17 15 views
62

En C#, ¿hay alguna manera de instanciar una instancia de una clase sin invocar su constructor?¿Crear instancia de objeto sin invocar al constructor?

Supongamos que la clase es pública y está definida en una biblioteca de terceros y el constructor es interno. Las razones por las que quiero hacer esto son complicadas, pero sería útil saber si es posible usar algún tipo de hackeo de C#.

NOTA: Específicamente no quiero llamar a ningún constructor, por lo que usar el reflejo para acceder al constructor interno no es una opción.

+3

Nota relacionada con esto y algunas personas tienden a no darse cuenta es que si lanza una excepción durante la construcción, su objeto aún existe ... si tiene un finalizador, que aún se ejecutará, debe tener cuidado al no hacer suposiciones sobre lo que sucedió en el constructor. – meandmycode

+0

¿Por qué querrías esto alguna vez? Si solo es curiosidad, está bien. – nawfal

+0

@nawfal WCF usa esto para su motor de serialización, que es una de las razones por las que es útil en general. Mi caso específico era realmente jugar con un modelo de objetos existente (el OM de SharePoint) para ver si era posible solucionar algunos errores/limitaciones con fines experimentales. Muy hacky seguro, pero de nuevo, experimental. –

Respuesta

78

No he intentado esto, pero hay un método llamado FormatterServices.GetUninitializedObject que se utiliza durante la deserialización.

Observaciones de MSDN dice:

Debido a que la nueva instancia del objeto se inicializa a cero y no hay constructores se ejecutan, el objeto podría no representa un estado que se considera como válidos por que objeto.

+2

Realmente me pregunto por qué necesitas algo así ... – Hallgrim

+0

En mi caso, es solo para prototipos. No es el código real que alguien usaría para algo remotamente importante. –

+0

Estoy planeando usarlo para deserializar un objeto DataContract de un formato serializado no compatible. –

0

Podría ser posible acceder al constructor a través de la reflexión e invocarlo así (pero no estoy seguro de que funcione, ya que el constructor es interno, tendrá que probarlo). De lo que yo sé, no puede crear un objeto sin llamar al constructor.

+2

+1, no sé por qué alguien votó esta respuesta correcta. – StingyJack

-1

Consulte la función System.Activator.CreateInstance.

+0

Lamentablemente, esta es una respuesta adecuada. La pregunta fue editada después de esta publicación. – nawfal

0

EDIT: Has actualizado tu pregunta, quieres construir una clase sin un constructor. O llame a un "Constructor vacío" predeterminado.

Esto no se puede hacer, ya que el compilador no generará un constructor predeterminado si ya hay uno especificado. Sin embargo, para el beneficio de los lectores, aquí es cómo llegar a un constructor interno, protegido o privado:

Asumiendo que su clase se llama Foo:

using System.Reflection; 

// If the constructor takes arguments, otherwise pass these as null 
Type[] pTypes = new Type[1]; 
pTypes[0] = typeof(object);  
object[] argList = new object[1]; 
argList[0] = constructorArgs; 

ConstructorInfo c = typeof(Foo).GetConstructor 
    (BindingFlags.NonPublic | 
    BindingFlags.Instance, 
    null, 
    pTypes, 
    null); 

Foo foo = 
    (Foo) c.Invoke(BindingFlags.NonPublic, 
        null, 
        argList, 
        Application.CurrentCulture); 

feo, pero funciona.

Por supuesto, puede haber una razón perfectamente legítima para marcar un constructor como interno, por lo que realmente debería considerar la logística de lo que desea antes de abusar de esa clase al obtenerla con reflexión.

+0

¿No acabas de llamar al constructor sin crear una instancia de objeto? (Por ejemplo: ¿exactamente lo contrario de lo que pregunta?) –

+0

Creé el objeto llamando al constructor oculto, que es lo que quería hasta que editó su publicación. – FlySwat

+0

El historial de revisión de la pregunta no está de acuerdo contigo. Tal vez esto es sucio para intentar optimizar las cosas demasiado. Recuerdo en un podcast que mencionó simplemente descartando cualquier registro de cambios que ocurrieron a los pocos minutos de haber publicado la pregunta –

0

Lo que está pidiendo hacer es una violación de la filosofía sobre la cual se desarrolló la programación administrada. .Net Framework y C# están construidos con el principio de que, siempre que sea posible, los objetos deben abstraerse de su memoria subyacente. Los objetos son objetos, no una matriz estructurada de bytes. Esta es la razón por la cual no puedes lanzar objetos a punteros vacíos de cualquier manera. Cuando los objetos se abstraen de su memoria subyacente, es fundamentalmente inválido sugerir que una instancia de objeto puede existir sin invocar al constructor.

Dicho esto, el .Net framework ha hecho concesiones al hecho de que, en realidad, los objetos están realmente representados en la memoria. Con algo de creatividad, es posible crear instancias de tipos de valores sin invocar sus inicializadores. Sin embargo, si sientes que debes hacerlo, probablemente estés haciendo las cosas mal.

-3

Nadie aquí ha aclarado lo que significa "No se puede hacer."

El constructor es lo que crea el objeto de su pregunta es similar a. '¿Cómo puedo construir un castillo de arena sin darle forma como un castillo' Puedes no? - Todo lo que tenemos es un montón de . arena

lo más cerca que sea posible podría hacer es asignar un bloque de memoria del mismo tamaño que el objeto:

byte[] fakeObject = new byte[sizeof(myStruct)]; 

(NOTA: incluso que sólo funcionará en MyStruct es un tipo de valor)

0

Tienes que llamar a un constructor para crear un objeto. Si no hay ninguno disponible a su gusto, tal vez podría usar una biblioteca de reescritura de código de bytes como Cecil del proyecto Mono. Funciona tanto en Windows como en Linux. De algunas de las demos que vi, se veía bastante genial. Puede cambiar los niveles de protección de métodos y todo tipo de cosas locas.

5

Al contrario de lo que muchos creen, un constructor no tiene mucho que ver con la instanciación de un objeto en absoluto (término bastante confuso). Un constructor es un método especial que se puede invocar después de la instanciación de un objeto para permitir que ese objeto se inicialice correctamente. En C++, la instanciación de objetos asigna memoria para el objeto, en .NET y Java se asigna y preinicializa a valores predeterminados dependiendo del tipo de campos (0, nulo, falso, etc.). Luego, el tiempo de ejecución llama al constructor. El nuevo operador encapsula estas dos acciones separadas en lo que parece ser una sola operación. La deserialización nunca podría haber funcionado en .NET si no hubiera sido posible crear una instancia sin usar un constructor. Dicho esto, el llamado tipo ConstructorInfo actúa como un nuevo operador y constructor cuando llama a su método Invoke (...).

-1

Me di cuenta de la "muerte" del tema, pero solo para aclarar a otros lectores y para poner una respuesta final que tal vez no fue posible cuando se publicó la pregunta. Aquí va.

Parece que puede instanciar una clase sin usar sus constructores asignando valores a sus propiedades. Aquí está la dirección donde se encuentra el instructivo en MSDN para este tipo de instanciación http://msdn.microsoft.com/en-us/library/bb397680.aspx.

Parece que esta es una técnica que no es muy conocida porque la encontré en un artículo en CodeProject y luego la busqué en Google y no encontré nada al respecto y luego visité MSDN Homepage y hubo una publicación vinculada yo a ese tema exacto. Así que parece que es un tema desconocido en lugar de uno nuevo porque la fecha en la publicación de CodeProject data de mayo de 2008.

Espero que esto ayude a alguien más a que google sea así y se presente con esta pregunta.

+2

Eso está mal. Se seguirá llamando a un constructor sin parámetros. Si solo hay un constructor s con parámetros, esto no puede y no compilará. MSDN es engañoso: no es "sin llamadas" solo porque no lo escribe. – Jabe

+0

El artículo enlazado de MSDN es muy claro al respecto: "El compilador procesa los inicializadores de objetos accediendo primero al constructor de instancia predeterminado y procesando las inicializaciones de los miembros. Por lo tanto, si el constructor predeterminado se declara como privado en la clase, los inicializadores de objetos requieren acceso público fallará ". –

0

Si la clase (y las clases de objetos a los que hace referencia) es Serializable, puede crear una copia profunda serializando usando un BinaryFormatter que emita un MemoryStream (creando un byte de matriz de bytes []), luego deserializando.Ver the answers to this question on converting an object to a byte array. (Pero tenga en cuenta: guardar la matriz de bytes para usar más tarde/en otro lugar probablemente no funcione. IOW no guarde la matriz de bytes en un archivo u otra forma persistente.)

Cuestiones relacionadas