Cet article se veut un petit exemple illustrant les propriétés et certains opérateurs en C#, le tout à partir d'une classe s'y prêtant : une classe Rationnel, dont chaque instance représente un élément de la forme où . J'ai escamoté la gestion du signe d'un Rationnel pour que le tout reste simple (mais vous pouvez corriger cet irritant si vous le souhaitez!).
Un petit exemple de programme de test pour tout cela serait celui proposé à droite (https://dotnetfiddle.net/hPBpWN) . Ce n'est, bien sûr, qu'une ébauche sans prétention. |
|
Puisqu'un Rationnel représente une division entière symbolique, le problème potentiel de la division par zéro doit être pris en charge. Sans grande surprise, dans ce programme, une tentative de division par zéro sur un Rationnel mènera à une levée d'exception. |
|
Il arrivera que nous souhaitions simplifier un Rationnel , et il se trouve que cette simplification se calcule bien par une application d'un algorithme de recherche du plus grand commun diviseur entre le numérateur et le dénominateur . Plus précisément : si , alors . Ainsi, la méthode privée Pgcd(a,b) nous aidera à réaliser cette opération lorsque cela s'avèrera opportun. |
|
Le numérateur est implémenté ici sous forme de propriété automatique, ne demandant aucune validation. Le dénominateur, lui, est plus détaillé puisque son implémentation implique une validation (il ne peut être nul). |
|
Nous supportons deux formes de construction, soit celle recevant à la fois un numérateur et un dénominateur et celle ne recevant qu'un numérateur (le dénominateur étant alors implicitement 1). Notez que, tel que mentionné plus haut, nous avons fait abstraction de la question de la gestion du signe. |
|
La simplification d'un Rationnel se fait tel que décrit plus haut (trouve un plus grand commun diviseur du numérateur et du dénominateur, puis retourner un Rationnel identique à l'original, à ceci près que son numérateur et son dénominateur sont tous deux divisés par cette valeur. Ce n'est pas à strictement parler nécessaire, mais c'est amusant. |
|
Les opérateurs sont des méthodes de classe (qualifiées static). Le langage C# est un peu... oppressant, du moins à mes yeux, en ce que pour implémenter ==, il faut implémenter != (ce qui est raisonnable), mais aussi (du moins si on souhaite respecter les usages) Equals(object) (ce qui est acceptable) et GetHashCode() (ce qui, sans être inacceptable, est un peu abusif). Les méthodes polymorphiques (qui, comme en C++, doivent être explicitement qualifiées virtual, même si notre exemple ne le montre pas ici), lorsque surchargées, doivent l'être de manière explicite, avec le mot clé override (que vous voyez ici apposé à GetHashCode() et à Equals()). Dans l'implémentation d'Equals(object o), remarquez le test pour valider que le paramètre o soit non nul, et (puisque Equals() prend un object en paramètre) la validation (downcast) que obj mène au moins vers un Rationnel, à l'aide de l'opérateur is. |
|
Les opérateurs relationnels définissant un ordonnancement peuvent aussi être surchargés. Le truc pour se simplifier l'existence, réduire l'effort d'entretien de code source et éviter d'introduire accidentellement des opérations qui ne respectent pas les règles usuelles de l'artihmétique est d'écrire un des quatre opérateurs en question et d'exprimer les trois autres en termes de celui-ci et de la négation logique. Habituellement, on codera d'abord l'opérateur <, puis le reste suivra. |
|
Il est bien sûr possible d'implémenter les opérateurs arithmétiques. J'ai exprimé l'opérateur d'addition binaire, à droite; vous pourrez vous en inspirer pour compléter le portait si cela vous semble opportun. |
|
Lorsqu'un objet est utilisé là où une string pourrait être utilisée, C# peut en tirer profit et utiliser, si cela s'avère opportun, la méthode ToString() de l'objet en question. Cette technique est mise en application à quelques reprises dans le programme principal. |
|
Enfin, il est possible d'exprimer le code à exécuter lors d'un transtypage (un Cast) sur un Rationnel. Ce transtypage peut être implicite (mot clé implicit), donc réalisé par le compilateur lorsqu'il le juge opportun, mais cela peut jouer des tours. Il peut aussi être explicite (notre choix ici). Détail technique : le transtypage se fait comme en C ou en Java, au sens où (T)expr demande de traiter l'expression expr comme étant de type T. |
|
Quelques liens pour enrichir le propos.