3 commentaires dimanche 10 avril 2011

Je me devais de faire part de la découverte suivante.

En C#, l'opérateur as permet de combiner deux opérations communes: tester si un objet est convertible en un type, et effectuer la conversion à proprement parler. Si la conversion échoue, ou si la référence est déjà nulle, as retourne null. Ainsi:


var val = ...;

// les deux bouts suivants sont équivalents:
var covt1 = val as Type;
var covt2 = val is Type ? (Type)val : null;


Un bon exemple serait une redéfinition de la méthode Equals:

class T
{
// ...

public override bool Equals(object obj)
{
var other = obj as T;
if(other == null)
return false; // couvre obj == null et !(obj is T)

// utiliser other pour la comparaison
}
}


En ce sens, c'est un équivalent direct du dynamic_cast de C++, lorsque celui-ci est utilisé sur des pointeurs.

Il y a néanmoins un défaut avec ce opérateur: as retourne null quand la conversion échoue, et null n'est pas une valeur valide pour les types valeur (les struct). On ne peut donc pas utiliser as pour caster un struct.

En pratique, ce n'est pas trop problématique: les structs héritent tous de System.ValueType, sont implicitement sealed, et ne peuvent donc pas faire partie d'une hiérarchie de classe. Les downcastings de ce genre sont rares.

Mais il ne faut pas oublier un détail: un struct hérite également de System.Object, et il possible de faire un downcasting de object à un struct (une procédure appelée "unboxing"). Pour ces cas, donc, j'en étais réduit à l'approche verbeuse (is + cast).

Or, j'ai récemment découvert quelque chose d'intéressant: il est possible d'utiliser as avec un type valeur si celui-ci est nullable:

struct V
{
// ...

public override bool Equals(object obj)
{
var other = obj as V?; // le ? est un raccourci pour Nullable<V>
if(!other.HasValue)
return false;

// utiliser other.Value pour la comparaison
}
}


MSDN n'en parle pas, mais c'est très intéressant. Testé avec C# 4.0, donc ce n'est peut-être pas disponible pour les versions précédentes. Nullable ayant été introduit en .NET 2.0, il est certain que ça ne fonctionnera pas avant.