9
votes

Passer le type d'objet actuel dans l'appel du constructeur de base

Comment puis-je saisir le type de la classe héritée et transmettez-le dans le constructeur de base de la classe également hérité? Voir l'exemple de code ci-dessous: xxx

la ligne la base (typeof (this) nom, myObject) ne fonctionne pas car je ne peux pas référence Ce pourtant, car l'objet n'a pas fini de construire et n'existe donc pas.

est-il possible de saisir le type de l'objet de construction actuellement? / p>

edit:

corrigé l'échantillon comme Orsogufo a suggéré , mais ne fonctionne toujours pas, comme ce est indéfini.

EDIT 2:

Juste pour clarifier, je veux me retrouver avec "héritédclass" étant transmis dans le Trèsbaseclass (String ClassName, MyObject MyObject) Constructeur.


3 commentaires

Pourquoi veux-tu faire cela? Un constructeur ne devrait pas se soucier de savoir s'il est appelé pour instancier sa propre classe ou une classe dérivée.


Je pense que vous manquez le point. L'assemblage externe qui définit la classe de base d'un cadre que j'utilise a nécessité la représentation des chaînes de la classe d'elle-même à transmettre dans le constructeur. Jusqu'à présent, je l'ai mis comme vous l'attendez (en tant que chaîne), mais je préférerais si cela le ferait de l'exécution à l'heure de la santé de Sanity.


Oh je vois, ce n'est pas votre classe. Je pense toujours que c'est une mauvaise chose que ça se soucie :)


6 Réponses :


3
votes

fait gettype (). Nom ne fonctionne pas?


8 commentaires

Non, car cela utilise implicitement "ceci" que vous ne pouvez pas utiliser pour évaluer les arguments de chaînage constructeur.


Désolé, je ne voulais pas dire dans une chaîne de constructeur, je voulais dire que le constructeur de tresbaseclass pourrait utiliser gettype (). Nom plutôt que d'attendre qu'il soit passé.


@PDR: Cela suppose que TrèsBaseclass veut toujours le nom du type actuel; Bien que cela puisse être un design raisonnable dans certains cas, ce n'est pas toujours. Je regarde cela comme une question plus large de "Que faites-vous lorsque vous souhaitez utiliser ceci dans un argument de chaîne constructeur" - car vous ne pouvez pas toujours pousser la fonctionnalité dans l'arbre d'héritage.


@PDR: Malheureusement, le tresbaseclass ne peut pas être modifié à ce stade. Je ne peux que modifier les deux autres classes :(


Ok, je vois ton point. J'essayais de résoudre le problème du questionneur. Je suggérerais que si vous voulez utiliser «cela» dans un argument de la chaîne de constructeur, vous devriez probablement penser difficile à la refonte.


@Codesleuth - OK, ce serait un problème. Je pense que vous devez probablement rester avec des cordes :(. Et avoir un mot avec les personnes qui ont fourni debaseclass


@PDR: Je me suis plaint à eux avant, mais en vain. Ils ont quitté la société avec une application déjà construite sur le cadre. Typique!


@Codesléuth: Je sens ta douleur, vraiment je fais



3
votes

Une solution possible: xxx


5 commentaires

Désolé, tu as raison. Ce.getType aurait dû être mon exemple. Hélas, Ce est indéfini.


Ah, ok ... eh bien, j'ai mis à jour mon message avec une solution possible (même sinon très facile à utiliser) ...


Cela devient plus difficile si vous souhaitez ajouter une autre couche d'héritage, et il est placé la logique d'utiliser type.Name dans la classe de base au lieu de la classe dérivée. (Dans la question, cette logique est dans la couche intermédiaire des trois.)


@Jon: Merci pour la remarque, j'ai ajouté la couche supplémentaire. Au fait, je pense que votre solution est meilleure, c'était juste une idée :)


Ok, maintenant, comment découlez-vous d'être dérivé? Vous devriez faire ce générique aussi ... c'est un problème de génériques en général :(



6
votes

J'ai déjà eu la même douleur avant maintenant dans le Google Wave Robot .NET API Où je voulais faire passer le constructeur dans certaines valeurs en fonction des attributs. Vous pouvez consulter ma solution dans le code pour le type dérivé et le Type de base . Fondamentalement, je transmettez un délégué au constructeur de base, puis appelez que le délégué transmettant «cela» au délégué. Donc, dans votre cas, vous auriez: XXX PRE>

et P>

public BaseClass() : base(FindClassName)
{
}

private static string FindClassName(VeryBaseClass @this)
{
    return @this.GetType().Name;
}


6 commentaires

Je n'ai jamais vu @Chis avant et semble confondre Google. Il suffit-il simplement d'appeler une méthode sans paramètres et de placer automatiquement l'objet actuel? (C'est comme ça que je le vois)


Non, le @ en face de celui-ci ne dit que le compilateur à considérer "ceci" comme identifiant (pour le distinguer du mot-clé). Vérifiez ceci: MSDN.MicRosoft.com/en-us /Library/aaa664670%28Vs.71%29.aspx . Vous pouvez remplacer '@Chis' avec n'importe quel nom de paramètre.


argument '1': impossible de convertir de "groupe de méthodes" à "chaîne" Je suppose que je devrais mentionner ici que, bien que mon exemple ne montre que le paramètre unique utilisé par la classe de base, ma mise en œuvre réelle utilise un peu de choses. Je vais mettre à jour la question pour le refléter.


@CODESLEUTH: Avez-vous modifié le constructeur TrèsBaseclass comme indiqué? Si tel est le cas, cela ne devrait plus vous attendre à une chaîne ...


Ah, je viens de voir que vous ne contrôlez pas la classe de base. Dans ce cas, cette solution ne fonctionnera pas pour vous - je modifierai ma réponse pour montrer que.


Awww, tu étais si proche! Je ne peux sûrement pas être la première personne qui a eu ce problème, il doit y avoir une résolution (ou une explication pourquoi il n'y a pas) quelque part.



0
votes

Une autre option ...

// VeryBaseClass is in an external assembly
public abstract class VeryBaseClass
{
    public VeryBaseClass(string className)
    {
        Console.WriteLine(className);
    }
}

// BaseClass and InheritedClass are in my assembly
public abstract class BaseClass : VeryBaseClass
{
    public BaseClass(string className)
        :
        base(className)
    {

    }
}

public class InheritedClass : BaseClass
{
    public InheritedClass()
        : base(typeof(InheritedClass).Name)
    {
    }
}


1 commentaires

Ce serait juste le même que de taper le nom de la classe en tant que chaîne. Je voulais éviter de devoir passer tout ce que je n'ai pas à entrer dans le constructeur basoclass à partir du héritédClass .



3
votes

Si la capacité de déterminer le type de classe héritée au moment de l'exécution est plus importante que la performance, vous pouvez jeter un coup d'œil sur la stacktrace pour voir où est appelé le constructeur. Vous devrez peut-être coder dans plus d'hypothèses lorsque vous appelez GetInheritedClassName dans cet exemple:

public abstract class BaseClass : VeryBaseClass
{
    public BaseClass(string className, MyObject myObject) :
        base(className, myObject)
    {

    }
}

public class InheritedClass : BaseClass
{
    public InheritedClass(MyObject myObject) : base("InheritedClass", myObject)
    {

    }
}


1 commentaires

J'aime la débrouillardité de cette réponse, mais vous avez raison d'être intensive de performance. Après avoir examiné mon code, je peux voir que je ne construisant pas beaucoup de ces objets à une fois à un moment donné de mon code, c'est donc définitivement viable. Définitivement une réponse +1 :)



19
votes

ah hah! J'ai trouvé une solution. Vous pouvez le faire avec des génériques:

public abstract class VeryBaseClass
{
    public VeryBaseClass(string className, MyObject myObject)
    {
        this.ClassName = className;
    }

    public string ClassName{ get; set; }
}
public abstract class BaseClass<T> : VeryBaseClass
{
    public BaseClass(MyObject myObject)
        : base(typeof(T).Name, myObject)
    {
    }
}

public class InheritedClass : BaseClass<InheritedClass>
{
    public InheritedClass(MyObject myObject) 
        : base(myObject)
    {

    }
}


5 commentaires

Oh mon Dieu, génie! Je vais le tester quand j'ai fini de boucher mon disposition de contrôle source TFS :)


Excellent, ça marche! Bien que cela signifie que j'ai beaucoup de travail à faire pour changer mes cours existants, mais cela en vaudra la peine à la fin. Merci :)


Soudainement, le CRTP (aka curieusement récurrent modèle de modèle) apparaît! :) (voir en.wikipedia.org/wiki/curiosité_recurring_template_pattern )


Avec le code ci-dessus, vous pouvez créer une classe comme celle-ci, où vous passez quelque chose de différent de la classe prolongée. Classe publique HerriditeClass: BaseClass {..} Pour appliquer que seul le type de classe étendue est transmis en vous pourrait aimer ajouter une clause où. Public abstrait classe BASECLASS : Trèsbaseclass où T: BASECLASS {...}


Le problème avec cette technique est qu'il n'y a aucune garantie que typeof (t) identifiera toujours correctement le type de type de l'objet en construction. Par exemple, cette réponse échoue à toute classe héritée davantage au-delà de héritédclass . Marquer tous les héritiers immédiats tels que scellé ou ajout type -Accepting protégé constructeur (s) constructeur (s) tout au long de l'emploi pourrait aider, mais est inapplicable. Même l'ajout du suggéré par @timothy reste fragile, car cela dépend de chaque béton t provenant de basoclass , qui, Pour la même raison de base, n'est toujours pas nécessairement t .