Comment aliaser un nom de classe en C #, sans avoir à ajouter une ligne de code à chaque fichier qui utilise la classe?

87

Je souhaite créer un alias pour un nom de classe. La syntaxe suivante serait parfaite:

public class LongClassNameOrOneThatContainsVersionsOrDomainSpecificName
{
   ...
}

public class MyName = LongClassNameOrOneThatContainsVersionOrDomainSpecificName;

mais il ne se compilera pas.


Exemple

Remarque Cet exemple est fourni à titre indicatif uniquement. N'essayez pas de résoudre ce problème particulier en suggérant de modifier la conception de l'ensemble du système. La présence, ou l'absence, de cet exemple ne change pas la question d'origine.

Certains codes existants dépendent de la présence d'une classe statique:

public static class ColorScheme
{
   ...
}

Ce jeu de couleurs est le jeu de couleurs Outlook 2003. Je souhaite introduire un jeu de couleurs Outlook 2007, tout en conservant le jeu de couleurs Outlook 2003:

public static class Outlook2003ColorScheme
{
   ...
}

public static class Outlook2007ColorScheme
{
   ...
}

Mais je suis toujours confronté au fait que le code dépend de la présence d'une classe statique appelée ColorScheme. Ma première pensée a été de créer une ColorSchemeclasse dont j'hériterai de l'un Outlook2003ou l' autre Outlook2007:

public static class ColorScheme : Outlook2007ColorScheme
{
}

mais vous ne pouvez pas hériter d'une classe statique.

Ma prochaine pensée a été de créer la ColorSchemeclasse statique , mais make Outlook2003ColorSchemeet les Outlook2007ColorSchemeclasses non statiques. Ensuite, une variable statique de la ColorSchemeclasse statique peut pointer vers l'un ou l'autre des jeux de couleurs "vrais":

public static class ColorScheme
{
    private static CustomColorScheme = new Outlook2007ColorScheme();
    ...
}

private class CustomColorScheme 
{ 
   ...
}

private class Outlook2008ColorScheme : CustomColorScheme 
{
    ...
}

private class Outlook2003ColorScheme : CustomColorScheme 
{
   ...
}

mais cela m'obligerait à convertir une classe composée entièrement de couleurs statiques en lecture seule en propriétés remplaçables, puis ma ColorSchemeclasse aurait besoin d'avoir les 30 propriétés différentes getters thunk dans l'objet contenu.

C'est juste trop de frappe.

Ma prochaine pensée était donc d'aliaser la classe:

public static ColorScheme = Outlook2007ColorScheme;

Mais cela ne compile pas.

Comment puis-je aliaser une classe statique en un autre nom?


Mise à jour: quelqu'un peut-il ajouter la réponse "Vous ne pouvez pas faire cela en C #" , afin que je puisse marquer cela comme la réponse acceptée. Toute autre personne souhaitant obtenir une réponse à la même question trouvera cette question, la réponse acceptée et un certain nombre de solutions de contournement qui pourraient ou non être utiles.

Je veux juste clore cette question.

Ian Boyd
la source
vous pourriez aussi bien accepter la réponse de Chris, même si vous ne voulez pas l'implémenter
devio
2
Ce n'est pas la réponse, c'est une solution de contournement. La réponse est que vous ne pouvez pas - du moins jusqu'à ce que quelqu'un vienne et publie la syntaxe réelle pour le faire.
Ian Boyd
1
Pour quiconque vient ici, la réponse acceptée est incorrecte car le commentaire le mieux noté fonctionne très bien dans les projets VS 2010 et VS 2017 c # sur lesquels je travaille. L'espace de noms complet doit être utilisé pour spécifier la classe lors de la configuration de l'alias, mais une fois configuré, l'alias fonctionne dans sa portée définie.
J-Americano
J'ai dû lire la réponse d'Ian et ses commentaires en détail avant de comprendre ce qu'il cherchait. Il souhaite déclarer un alias de classe en un seul endroit , plutôt que de devoir l'ajouter en haut de chaque fichier faisant référence à la classe. Je ne connais pas de langues fortement typées qui prennent en charge cela. (Si quelqu'un connaît un tel langage, j'aimerais le savoir.) J'ai édité le titre pour le rendre plus clair.
ToolmakerSteve
BTW, pour tous ceux qui essaient de faire quelque chose de similaire: si ce sont des classes que vous définissez, alors l'approche C # consiste à définir un interface, que toutes vos classes implémentent. Comme mentionné dans la réponse de chills42 . Vous pouvez alors définir un «service» ou une «usine» qui retourne un objet qui implémente cette interface, selon les circonstances actuelles (par exemple plate-forme / OS) ou sur un fichier de configuration.
ToolmakerSteve

Réponses:

129

Vous ne pouvez pas . La prochaine meilleure chose que vous puissiez faire est d'avoir des usingdéclarations dans les fichiers qui utilisent la classe.

Par exemple, vous pouvez réécrire le code dépendant en utilisant un alias d'importation (comme quasi- typedefsubstitut):

using ColorScheme = The.Fully.Qualified.Namespace.Outlook2007ColorScheme;

Malheureusement, cela doit aller dans chaque portée / fichier qui utilise le nom.

Je ne sais donc pas si cela est pratique dans votre cas.

Konrad Rudolph
la source
9
S'il pouvait aller en haut du fichier contenant la classe d'origine: ce serait génial. Mais le usingdoit être ajouté à tout code cassé. Et cela annule également la valeur d'avoir un seul alias, ce qui me permet de basculer tous les utilisateurs de la ColorSchemeclasse existante vers une nouvelle classe - sans modifications. En d'autres termes: je veux alias la ColorSchemeclasse vers une autre classe.
Ian Boyd
Existe-t-il un moyen d'obtenir la même chose et de propager l'alias avec héritage. c'est-à-dire que toutes les classes étendant MyClass pourront utiliser ColorScheme au lieu du.Fully.Qualified ... ColorScheme en ajoutant simplement l'instruction using dans le fichier source MyClass?
Florian Burel
Eh bien, c'était très pratique pour mon cas. Enfin C # arrêtera de dessiner mes chemins de fichiers.
ElDoRado1239 le
25

Vous pouvez créer un alias pour votre classe en ajoutant cette ligne de code:

using Outlook2007ColorScheme = YourNameSpace.ColorScheme;
mohammedn
la source
Le nom 'ColorScheme' n'existe pas dans le contexte actuel
Ian Boyd
7
vous avez besoin de Fully.Qualified.Namespace.Of.ColorScheme
Jamie Pate
Je pensais qu'en C # ne pouvait alias que les espaces de noms (pas les classes) de cette façon, où dans VB.Net vous pouvez alias des espaces de noms ou des classes en utilisant Imports. Ai-je tort?
Nick
Vous n'avez pas besoin de nom complet si vous mettez une usingdirective dans son espace de noms, par exemple lorsque vous avez besoin d'un alias de votre propre classe.
Elvedin Hamzagic
12

Vous voulez un ( Factory | Singleton ), en fonction de vos besoins. Le principe est de faire en sorte que le code client n'ait pas à savoir quel jeu de couleurs il obtient. Si la palette de couleurs doit être étendue à l'application, un singleton devrait convenir. Si vous pouvez utiliser un schéma différent dans des circonstances différentes, un motif Factory est probablement la voie à suivre. Dans tous les cas, lorsque le jeu de couleurs doit changer, le code ne doit être changé qu'à un seul endroit.

public interface ColorScheme {
    Color TitleBar { get; }
    Color Background{ get; }
    ...
}

public static class ColorSchemeFactory {

    private static ColorScheme scheme = new Outlook2007ColorScheme();

    public static ColorScheme GetColorScheme() { //Add applicable arguments
        return scheme;
    }
}

public class Outlook2003ColorScheme: ColorScheme {
   public Color TitleBar {
       get { return Color.LightBlue; }
   }

    public Color Background {
        get { return Color.Gray; }
    }
}

public class Outlook2007ColorScheme: ColorScheme {
   public Color TitleBar {
       get { return Color.Blue; }
   }

    public Color Background {
        get { return Color.White; }
    }
}
Chris Marasti-Georg
la source
Cela ressemble moins à une usine qu'à une faible tentative de motif singleton. Une fabrique serait plus susceptible de paramétrer la méthode de création; vous auriez quelque chose de plus comme: public static ColorScheme GetColorScheme (descripteur de chaîne);
OwenP
C'est vrai - l'idée de base est de s'assurer que lorsque Office 2012 sortira, le code ne devra changer qu'à un seul endroit.
Chris Marasti-Georg
1
Cela fonctionne définitivement, mais c'est certainement une solution d'entreprise à un problème simple.
Ian Boyd
11

Vous ne pouvez pas aliaser un nom de classe en C #.

Il y a des choses que vous pouvez faire sans aliaser un nom de classe en C #.

Mais pour répondre à la question d'origine: vous ne pouvez pas aliaser un nom de classe en C #.


Mise à jour: les gens ne savent pas pourquoi usingcela ne fonctionne pas. Exemple:

Form1.cs

private void button1_Click(object sender, EventArgs e)
{
   this.BackColor = ColorScheme.ApplyColorScheme(this.BackColor);
}

ColorScheme.cs

class ColorScheme
{
    public static Color ApplyColorScheme(Color c) { ... }
}

Et tout fonctionne. Maintenant, je veux créer une nouvelle classe et lui attribuer un alias ColorScheme (afin qu'aucun code ne doive être modifié ):

ColorScheme.cs

using ColorScheme = Outlook2007ColorScheme;

class Outlook2007ColorScheme
{
    public static Color ApplyColorScheme(Color c) { ... }
}

Ohh, je suis désolé. Ce code ne compile pas:

entrez la description de l'image ici

Ma question était de savoir comment aliaser une classe en C #. Cela ne peut pas être fait. Il y a des choses que je peux faire sans aliaser un nom de classe en C #:

  • changer tous ceux qui dépendent de ColorSchemeà la using ColorSchemeplace (solution de contournement de changement de code car je ne peux pas créer d'alias)
  • changer tous ceux qui en dépendent ColorSchemepour utiliser un modèle d'usine, une classe ou une interface polymorphe (solution de contournement de changement de code car je ne peux pas créer d'alias)

Mais ces solutions de contournement impliquent de casser le code existant: pas une option.

Si les gens dépendent de la présence d'une ColorSchemeclasse, je dois en fait copier / coller une ColorSchemeclasse.

En d'autres termes: je ne peux pas aliaser un nom de classe en C #.

Cela contraste avec d'autres langages orientés objet, où je pourrais définir l'alias:

ColorScheme = Outlook2007ColorScheme

et j'aurais fini.

Ian Boyd
la source
21
Vous pouvez absolument alias un nom de classe en C #. "using <alias_name> = <fully_qualified_name>;"
clemahieu
7
LIke @clemahieu a dit, vous pouvez absolument alias un nom de classe, il vous suffit d'utiliser le nom complet. En outre, si votre alias est une classe générique, vous pouvez avoir à ajouter le qualificatif de classe générique. Par exemple: en utilisant ShortName = MyNamespace.SubNamespace.GenericClass <MyType>;
Dan Morphis
11
Downvote - vous avez déclaré "Vous ne pouvez pas créer d'alias pour un nom de classe en C #." Vous pouvez. Ce que vous ne pouvez pas faire est d'aliaser une classe comme vous le souhaitez - ce qui est une exigence tout à fait justifiée, mais votre déclaration est incorrecte.
Tom W
8
Comme plusieurs affiches l'ont souligné, le fait de devoir utiliser le nom qualifié n'interdit en aucun cas les alias. Vous pouvez alias la classe. Il vous suffit d'utiliser le nom complet pour ce faire. Vous pouvez trouver cela gênant, mais cela ne rend pas la déclaration «Vous ne pouvez pas aliaser un nom de classe en C #» vraie. Peut-être que la façon dont les alias fonctionnent en C # est différente de ce à quoi vous vous attendiez. C'est bien - si c'est le cas, dites-le. Mais vous pouvez aliaser un nom de classe en C # car la spécification stipule que vous pouvez le faire, selon la définition qu'elle fournit.
Tom W du
5
J'aime la façon dont nous, les programmeurs, allons faire nos déclarations entre les lignes. Ce que Ian dit vraiment, c'est que C # est stupide et cassé car il ne peut pas faire une chose de base simple que n'importe qui voudrait faire et devrait être capable de faire. Il a raison - si une sémantique spécifique vous rend heureux ou non.
IQpierce
10

essaye ça:

using ColorScheme=[fully qualified].Outlook2007ColorScheme
Dpurrington
la source
Le nom 'ColorScheme' n'existe pas dans le contexte actuel
Ian Boyd
2
vous avez besoin de Fully.Qualified.Namespace.Of.ColorScheme
Jamie Pate
6

J'ajoute ce commentaire pour les utilisateurs qui le trouvent longtemps après que OP ait accepté leur "réponse". L'alias en C # fonctionne en spécifiant le nom de la classe à l'aide de son espace de noms complet. Une fois défini, le nom d'alias peut être utilisé dans sa portée. Exemple.

using aliasClass = Fully.Qualified.Namespace.Example;
//Example being the class in the Fully.Qualified.Namespace

public class Test{

  public void Test_Function(){

    aliasClass.DoStuff();
    //aliasClass here representing the Example class thus aliasing
    //aliasClass will be in scope for all code in my Test.cs file
  }

}

Toutes mes excuses pour le code rapidement tapé, mais j'espère qu'il explique comment cela devrait être implémenté afin que les utilisateurs ne soient pas induits en erreur en pensant que cela ne peut pas être fait en C #.

J-Americano
la source
Ce serait encore plus clair si vous montriez la déclaration de l'autre classe: namespace Fully.Qualified.Namespace{ public class Example {... public void DoStuff(){... }... } }.
ToolmakerSteve
1
Pour être clair, c'est différent de ce que recherche Ian. Ian a une situation où il ne peut pas (ou ne veut pas) modifier les fichiers source qui font référence à une classe. Il veut un moyen de faire une modification à un seul endroit de son application ou de sa bibliothèque , ce qui semble être une classe avec le nom souhaité, que tout autre code peut utiliser [sans avoir à ajouter cette instruction "using" à plusieurs sources fichiers - par exemple, cette source peut ne pas être disponible pour être modifiée].
ToolmakerSteve
4

Aliasing comme vous le souhaitez ne fonctionnera pas en C #. Cela est dû au fait que l'aliasing se fait via la usingdirective, qui est limitée au fichier / espace de noms en question. Si vous avez 50 fichiers qui utilisent l'ancien nom de classe, cela signifie 50 emplacements à mettre à jour.

Cela dit, je pense qu'il existe une solution simple pour rendre votre changement de code aussi minime que possible. Faites de la ColorSchemeclasse une façade pour vos appels aux classes réelles avec l'implémentation, et utilisez le usingdans ce fichier pour déterminer lequel ColorSchemevous utilisez.

En d'autres termes, procédez comme suit:

using CurrentColorScheme = Outlook2007ColorScheme;
public static class ColorScheme
{
   public static Color ApplyColorScheme(Color c)
   {
       return CurrentColorScheme.ApplyColorScheme(c);
   }
   public static Something DoSomethingElse(Param a, Param b)
   {
       return CurrentColorScheme.DoSomethingElse(a, b);
   }
}

Ensuite, dans votre code derrière, ne changez rien:

private void button1_Click(object sender, EventArgs e)
{
   this.BackColor = ColorScheme.ApplyColorScheme(this.BackColor);
}

Vous pouvez ensuite mettre à jour les valeurs de ColorSchemeen mettant à jour une ligne de code ( using CurrentColorScheme = Outlook2008ColorScheme;).

Quelques préoccupations ici:

  • Chaque nouvelle méthode ou définition de propriété devra alors être ajoutée à deux endroits, à la ColorSchemeclasse et à la Outlook2007ColorSchemeclasse. C'est un travail supplémentaire, mais s'il s'agit d'un vrai code hérité, cela ne devrait pas être fréquent. En prime, le code ColorSchemeest si simple que tout bogue éventuel est très évident.
  • Cette utilisation de classes statiques ne me semble pas naturelle; J'essaierais probablement de refactoriser le code hérité pour le faire différemment, mais je comprends aussi que votre situation ne le permet pas.
  • Si vous avez déjà une ColorSchemeclasse que vous remplacez, cette approche et toute autre pourraient être un problème. Je vous conseillerais de renommer cette classe en quelque chose comme ColorSchemeOld, puis d'y accéder via using CurrentColorScheme = ColorSchemeOld;.
Timothée
la source
3

Je suppose que vous pouvez toujours hériter de la classe de base sans rien ajouté

public class Child : MyReallyReallyLongNamedClass {}

MISE À JOUR

Mais si vous avez la capacité de refactoriser le classlui - même: Un nom de classe est généralement inutilement long en raison du manque de namespaces.

Si vous voyez des cas comme ApiLoginUser, DataBaseUser, WebPortalLoginUser, est généralement indication du manque de namespaceraison de la crainte que le nom Userpourrait entrer en conflit.

Dans ce cas cependant, vous pouvez utiliser un namespacealias , comme cela a été souligné dans les articles ci-dessus

using LoginApi = MyCompany.Api.Login;
using AuthDB = MyCompany.DataBase.Auth;
using ViewModels = MyCompany.BananasPortal.Models;

// ...
AuthDB.User dbUser;
using ( var ctxt = new AuthDB.AuthContext() )
{
    dbUser = ctxt.Users.Find(userId);
}

var apiUser = new LoginApi.Models.User {
        Username = dbUser.EmailAddess,
        Password = "*****"
    };

LoginApi.UserSession apiUserSession = await LoginApi.Login(apiUser);
var vm = new ViewModels.User(apiUserSession.User.Details);
return View(vm);

Notez comment les classnoms sont tous User, mais dans des namespaces différents . Citant PEP-20: Zen of Python :

Les espaces de noms sont une excellente idée - faisons-en plus!

J'espère que cela t'aides

percebus
la source
2
toujours, sauf si vous ne pouvez pas: par exemple classe scellée
mikus
mais cela ne fonctionnera pas dans de nombreux cas. par exemple la classe publique MyList: List {} - si vous essayez plus tard MyList xyz = quelque chose.ToList (); vous serez coincé.
Offler
@Offler Vous pourriez (et devriez probablement) recourir à l'un ou l'autre des newmots clés pour de telles méthodes ou même proposer vos propres méthodes d'extension, non? Dans tous les cas, je suis convaincu que vous devriez toujours utiliser des classes de collection vanilla (ie Dictionary, List, IEnumerable, IQueryable, etc.) avec des modèles / ViewModels / POCO personnalisés. Comme le dit Mvc: Convention over Configuration
percebus
@percebus ne fonctionne pas dans tous les cas. J'essaie de transformer le code vintage lorsque les parties utilisent une API qui n'est plus prise en charge par une nouvelle. le code de transformation durign sera modifié par d'autres et devrait toujours être exécutable - les deux devraient donc être utilisables maintenant. faire converger les choses sur 700.000 LOC (sans commentaires) tout en le laissant être exécutable serait plus facile, si un alias de style c ++ était possible - et vous n'avez besoin que d'un endroit dans un fichier pour remplacer une classe d'alias par l'implémentation de l'un ou l'autre.
Offler
RE-POST pour EDIT @Offler Pour ce que vous décrivez, vous avez besoin de quelque chose comme une usine avec des interfaces. IColorScheme oColorScheme = ColorSchemeFactory.Create(); Aussi, vous voudrez peut-être examiner l' injection de dépendance
percebus
2

Est-il possible de passer à l'utilisation d'une interface?

Peut-être pourriez-vous créer une IColorSchemeinterface que toutes les classes implémentent?

Cela fonctionnerait bien avec le modèle d'usine comme le montre Chris Marasti-Georg

frissons42
la source
C'est possible, mais je ne vais pas passer plus de temps dessus plutôt que de renommer la classe qui est le jeu de couleurs "actuel" à utiliser.
Ian Boyd
0

C'est une réponse partielle très tardive - mais si vous définissez la même classe 'ColorScheme', dans le même espace de noms 'Outlook', mais dans des assemblys séparés, l'un appelé Outlook2003 et l'autre Outlook2007, alors tout ce que vous avez à faire est de référencer l'assemblage approprié .

Mark Farmiloe
la source