74 votes

Créer une instance d'objet sans invoquer le constructeur ?

En C#, existe-t-il un moyen d'instancier une instance d'une classe sans invoquer son constructeur ?

Supposons que la classe soit publique et définie dans une bibliothèque tierce et que le constructeur soit interne. Les raisons pour lesquelles je veux faire cela sont compliquées mais il serait utile de savoir si c'est possible en utilisant une sorte de hacking C#.

NOTE : J'ai spécifiquement ne veulent pas appeler de constructeur Il n'est donc pas possible d'utiliser la réflexion pour accéder au constructeur interne.

1voto

Warty Points 4663

Voir RuntimeHelpers.GetUninitializedObject(Type) Disponible à partir de .NET Core 2.0 / .NET Standard 2.1.

Une autre réponse suggère FormatterServices.GetUninitializedObject(Type) qui se substitue directement aux RuntimeHelpers.

Pour être complet, si vous souhaitez créer un objet non initialisé du même type qu'une autre instance, dans .NET Core 7, il existe également une méthode non documentée, le AllocateUninitializedClone avec l'exemple de code ci-dessous. Je ne recommanderais pas cette méthode plutôt que de simplement passer instance.GetType() à l'API documentée.

De la même manière, voici des notes pour le cas où vous souhaiteriez réellement utiliser un constructeur interne :

Étant donné un Type et quelques arguments.

Utilisation Activator.CreateInstance(Type type, object[] args)) .

Alternativement via les API de réflexion

Utilisation type.GetConstructor . Le retour ConstructorInfo dispose d'un Invoke méthode. Si vous passez une instance, vous réinitialiserez effectivement un objet. Si vous ne passez pas d'instance, l'invocation instanciera un nouvel objet.

Quelques exemples de code :

using System.Reflection;
using System.Runtime.CompilerServices;

public static class Program {
   public static void Main() {
      //
      // create uninitialized object instance, then run ctor on it
      //
      var inst1 = (C)RuntimeHelpers.GetUninitializedObject(typeof(C));
      Console.WriteLine(inst1.X); // 0

      var ctor = typeof(C).GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, Array.Empty<Type>());
      ctor.Invoke(inst1, null); // Prints "Ran C's Ctor. X is -123"
      Console.WriteLine(inst1.X); // 2

      //
      // create new object with ctor (via reflection)
      //
      var inst2 = (C)ctor.Invoke(null); // Prints "Ran C's Ctor. X is -123"
      Console.WriteLine(inst2.X); // 2

      //
      // create new object with ctor (via activator)
      //
      var inst3 = (C)Activator.CreateInstance(typeof(C), BindingFlags.Instance | BindingFlags.NonPublic, null, null, null); // Prints "Ran C's Ctor. X is -123"
      Console.WriteLine(inst3.X); // 2

      //
      // create new uninitialized object of type matching a given instance via undocumented AllocateUninitializedClone
      //
      var allocateUninitializedClone = typeof(RuntimeHelpers).GetMethod("AllocateUninitializedClone", BindingFlags.Static | BindingFlags.NonPublic, new[] { typeof(object) });
      var inst4 = (C)allocateUninitializedClone.Invoke(null, new []{inst2});
      Console.WriteLine(inst4.X); // 0
   }

   public class C {
      private C() {
         Console.WriteLine($"Ran C's Ctor. X is {X}");
         X = 2;
      }

      public int X { get; } = -123;
   }
}

0voto

FlySwat Points 61945

EDIT : Vous avez mis à jour votre question, vous voulez construire une classe sans constructeur. Ou appeler un "Constructeur vide" par défaut.

Cela n'est pas possible, car le compilateur ne génère pas de constructeur par défaut s'il y en a déjà un de spécifié. Cependant, pour le bénéfice des lecteurs, voici comment accéder à un constructeur interne, protégé ou privé :

En supposant que votre classe s'appelle 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);

Moche, mais efficace.

Bien sûr, il peut y avoir une raison parfaitement légitime de marquer un constructeur comme interne, donc vous devriez vraiment considérer la logistique de ce que vous voulez avant d'abuser de cette classe en y accédant avec la réflexion.

0voto

rslite Points 17279

Il pourrait être possible d'accéder au constructeur par réflexion et de l'invoquer de cette manière (mais je ne suis pas sûr que cela fonctionnera puisque le constructeur est interne - vous devrez le tester). Sinon, à ma connaissance, il n'est pas possible de créer un objet sans appeler le constructeur.

0voto

Sam Corder Points 3196

Il faut appeler un constructeur pour créer un objet. S'il n'y en a pas de disponible à votre goût, vous pourriez peut-être utiliser une bibliothèque de réécriture de code octet comme Cecil du projet Mono. Elle fonctionne sous Windows et Linux. D'après les démonstrations que j'ai vues, il avait l'air plutôt cool. Vous pouvez modifier les niveaux de protection des méthodes et toutes sortes de choses folles.

0voto

Art Swri Points 1168

Si la classe (et les classes d'objets qu'elle référence) est sérialisable, vous pouvez créer une copie profonde en sérialisant à l'aide d'un BinaryFormatter qui émet vers un MemoryStream (en créant un tableau d'octets byte[]), puis en désérialisant. Voir les réponses à cette question sur la conversion d'un objet en tableau d'octets . (Mais attention, sauvegarder le tableau d'octets pour l'utiliser plus tard/ailleurs ne fonctionnera probablement pas. En d'autres termes, ne sauvegardez pas le tableau d'octets dans un fichier ou sous une autre forme persistante).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X