2010-11-12 38 views
18

Si je définis une classe avec un constructeur privé par défaut et un constructeur public qui a des paramètres, comment puis-je accéder au constructeur privé?Accéder à un constructeur privé depuis l'extérieur de la classe en C#

public class Bob 
{ 
    public String Surname { get; set; } 

    private Bob() 
    { } 

    public Bob(string surname) 
    { 
     Surname = surname; 
    } 
} 

Je peux accéder au constructeur privé via une méthode statique de la classe comme ceci:

public static Bob GetBob() 
{ 
    return new Bob(); 
} 

Je pensais que je pouvais accéder au constructeur privé via une méthode d'extension, car (selon ma compréhension) les méthodes d'extension sont convertis afin qu'ils semblent être méthodes statiques sur la classe, mais je ne peux pas:

static class Fred 
{ 
    public static Bob Bobby(this Bob bob) 
    { 
     return new Bob(); 
    } 
} 

Alors, comment puis-je accéder au constructeur privé?

Merci


EDIT:

La raison pour laquelle je voulais faire était que je voulais créer des tests pour une de nos classes d'affaires, mais pas permettre à un consommateur de cette classe être capable d'instancier un objet de manière incorrecte. Je le teste, donc je sais (j'espère!) Dans quelles circonstances les tests échoueront. Je suis toujours un test n00b en ce moment, donc mon idée peut ou ne pas avoir été la "mauvaise" façon de faire les choses. J'ai changé ma stratégie de test pour faire les choses comme le ferait un consommateur de cette classe, c'est-à-dire en appelant les méthodes publiques et si les méthodes publiques sont correctes, en supposant que les méthodes privées sont OK. Je préférerais encore tester les méthodes privées, mais mon patron est respire dans mon cou sur un livrable :-(

+8

Pourquoi le vote serré? C'est toujours une question valide, même s'il n'est pas recommandé d'obtenir le constructeur. – jgauffin

+0

D'accord - encore une question valable, pourquoi fermer? –

+0

Re: Test de méthodes privées. J'ai trouvé qu'en testant individuellement des méthodes privées, mes tests sont plus petits et plus précis que si je ne faisais que tester des méthodes publiques. J'ai tendance à rendre 'private' dans 'protected' et à écrire une classe wrapper spécifique dans la bibliothèque de test pour les exposer comme des versions publiques plutôt que d'utiliser la réflexion. –

Répondre

37

Les constructeurs par défaut sont privés pour une raison.Le développeur ne le rend pas privé pour le plaisir

Mais si vous voulez toujours utiliser le constructeur par défaut, vous l'obtenez en utilisant la réflexion.

var constructor = typeof(Bob).GetConstructor(BindingFlags.NonPublic|BindingFlags.Instance, null, new Type[0], null); 
var instance = (Bob)constructor.Invoke(null); 

Modifier

J'ai vu vos commentaires sur les tests. Ne jamais tester des méthodes/propriétés protégées ou privées. Vous avez probablement fait quelque chose de mal si vous ne parvenez pas à tester ces méthodes/propriétés via l'API publique. Retirez-les ou refactorisez la classe.

Modifier 2

oublié un drapeau de liaison.

+1

N'est-ce pas une sorte de piratage? Pour contourner la raison de rendre le constructeur privé? –

+6

Oui c'est. C'est pourquoi j'ai écrit * Les constructeurs par défaut sont privés pour une raison. Le développeur ne le rend pas privé pour le plaisir. * – jgauffin

+1

Merci. Toutefois, la méthode 'GetConstructor' renvoie null, de sorte que le code lève une exception NullReferenceException. 'typeof (Bob) .GetConstructors (BindingFlags.NonPublic);' retourne un tableau vide. – AndrewJacksonZA

0

La méthode Bobby est encore dans une classe différente, appelée Fred.C'est pourquoi vous ne pouvez pas Accédez au constructeur prive de la classe Bob.Ce que vous essayez de faire n'est pas possible avec les méthodes attachées.Même si elles peuvent être attachées à une autre classe, elles sont toujours déclarées en dehors de cette classe et suivent les règles d'accès/portée habituelles

+0

Merci pour votre contribution. J'ai inclus 'Bobby' pour montrer ce que j'ai fait afin que personne ne gaspille son temps à suggérer des méthodes d'extension. – AndrewJacksonZA

0

Vous pouvez instancier des instances de ce type par réflexion.

2

Il y a plusieurs façons de contourner ce problème:

Un: Faire le public du constructeur. Si vous avez besoin d'y accéder depuis l'extérieur de la classe, pourquoi est-ce privé (il se peut que vous vouliez seulement accéder au constructeur privé pour le tester, auquel cas c'est un problème valide).

Deux: Faire le constructeur protégé, alors l'accès à travers une classe dérivée:

public class Bob 
{ 
    public String Surname { get; set; } 

    protected Bob() 
    { } 

    public Bob(string surname) 
    { 
     Surname = surname; 
    } 
} 

public class Fred : Bob 
{ 
    public Fred() 
     : base() 
    { 
    } 
} 

Trois: réflexion utilisation (comme le montre jgauffin).

+0

Comme vous pouvez le voir dans l'édition sur la question, c'est pour tester, merci. Et le formatage est toujours sorti. – AndrewJacksonZA

+1

les bogues de formatage de code lors de l'utilisation de listes dans la même réponse. – jgauffin

+0

Cheers - Je vais essayer de m'en souvenir pour l'avenir. –

1

En plus à la réponse de @ jgauffin qui raconte comment appeler les constructeurs privés par réflexion:

Est-il possible de changer les modificateurs du constructeur privé?

Il semble que vous implémentiez Factory Pattern dans votre code. Ainsi, le modificateur est censé être internal.

public class Product 
{ 
    //others can't create instances outside the namespace 
    internal Product() { }  
} 
public class ProductProvider 
{ 
    //they can only get standardized products by the provider 
    //while you have full control to Product class inside ProductProvider 
    public static CreateProduct() 
    { 
     //can create an instance here because they are in the same namespace 
     Product p = new Product();  
     //standardize the product 
     return p; 
    } 
} 

Méthodes d'extension

public static MyExt 
{ 
    public static void DoSomething(this Product p) { } 
} 

Appel p.DoSomething() est égal en fait MyExt.DoSomething(p). Il ne met pas cette méthode dans la classe Produit.

+0

Juste une note - le modificateur d'accès "internal" n'est pas une restriction au niveau de l'espace de noms mais au niveau de l'assemblage. voir: https: //msdn.microsoft.com/en-us/library/7c5ka91b.aspx. Vous pourriez aussi simplement mettre la méthode d'usine statique dans la classe Product elle-même. – RhysC

0
public class demo 
{ 
    private demo() 
    { 
     Console.WriteLine("This is no parameter private constructor"); 
    } 
    public demo(int a) 
    { 
     demo d = new demo('c');// u can call both private contstructors from here 
     demo dd = new demo(); 
     Console.WriteLine("This is one parameter public constructor"); 
    } 
    private demo(char a) 
    { 
     Console.WriteLine("This is one parameter public constructor::" + a); 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     demo obj = new demo(7); 
     // demo obj = new demo(); // it will raise error 
     Console.ReadLine(); 
    } 
} 
+0

Veuillez commenter votre solution –

0

Si vous utilisez noyau dotnet vous pouvez faire ce qui suit sans même avoir à jouer avec toute réflexion:

YourCustomObject player = (YourCustomObject)Activator.CreateInstance(typeof(YourCustomObject),true);