Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

CoqBlog

.NET is good :-)
{ Blog de Gaël Covain }

Actualités

Outils : BabelPad, éditeur de texte Unicode supportant entres autres UTF-32 (Little Endian comme Big Endian)

Si vous avez un jour cherché un éditeur de texte pouvant enregistrer en utilisant un encoding UTF-32, vous serez peut être intéressé par le freeware (ou "gratuiciel" dans la langue de Molière) sur lequel je suis tombé aujourd'hui : BabelPad.

Il gère une bonne liste de formats d'encodage en entrée, et supporte en sortie la "gamme UTF" qui va bien, avec ou sans BOM (Byte Order Mark) :

  • UTF-8
  • UTF-16 Little Endian ou Big Endian
  • UTF-32 Little Endian ou Big Endian

Plus quelques autres encoding (?) ne me parlant absolument pas, je cite :

  • GB18030 (with or without a Byte Order Mark)
  • ASCII with Hexadecimal Numeric Character Reference (NCR) substitution of non Basic Latin characters
  • ASCII with Decimal Numeric Character Reference (NCR) substitution of non Basic Latin characters
  • ASCII with Universal Character Name (UCN) substitution of non Basic Latin characters
  • ASCII with HTML Entity substitution of non Basic Latin characters

 

Bon, au vu du site BabelStone de l'auteur (Andrew West) et des informations trouvées sur lui, limiter la description de l'outil à sa capacité d'export semble assez réductrice :p

A titre d'exemple je vais me contenter (ne voyant pas comment faire mieux) de reproduire ici la capture disponible sur le site, qui illustre plutôt bien ce que sait faire ce sympathique outil :

BabelPad (Unicode Text Editor for Windows)

Pour plus d'informations je vous invite à visiter la page de l'outil ou le post qui lui est consacré sur le blog de l'auteur.

 

Au passage vous pouvez aussi jeter un oeil à BabelMap.
La fonction Character Map en elle même est aussi disponible directement dans BabelPad.
Si vous ne lui voyez pas d'intérêt, essayez de placer le caractère "Line Separator" (U+2028) dans votre presse-papier en utilisant la table de caractères (charmap.exe) livrée de base avec Windows ;-)

BabelPad - Unicode Character Map

SOS et DGML main dans la main : GCRootToDGML

Découvert à l'instant via John Robbins.

Je pense que toute personne ayant un jour tapée la commande "!gcroot" appréciera la vidéo et l'outil disponibles sur ce post de Chris Lovett : Visualizing Runtime Object Graphs

Je ne l'ai pas encore encore testé en situation réelle (et j'ai heureusement très peu d'occasions d'utiliser gcroot) mais je m'en voudrais d'avoir perdu le lien à ce moment là !

Bon ok, pour DGML il faut Visual Studio 2010. Mais du coup en partant de cette idée là il doit être possible de produire des graphes avec Graphviz (directement ou via QuickGraph par exemple) ou autre solution de rendu de graphes.

Panopticlick : vous pensiez ne pas laisser d'empreinte spécifique en surfant ?

Je viens de le découvrir par l'intermédiaire de Patrick : le projet Panopticlick de l'Electronic Frontier Foundation (EFF) est plutôt intéressant.
Il met en avant le fait que même en laissant de côté les cookies, notre navigateur peut tout de même permettre de nous identifier plus ou moins précisément d'une visite à l'autre.
Ou du moins permettre de nous classer dans un groupe particulier de personnes ayant un profil similaire via une combinaison d'éléments tels que le contenu de la chaine UserAgent, la liste des fonts, la liste des plugins du navigateur, etc

Ce genre d'information de profil type peut se révéler plutôt intéressant pour la présentation de publicités ciblées par exemple.
Côté sécurité la divulgation de numéros de versions peut aussi donner une idée du niveau de maintenance de la machine, et permettre de rendre plus efficace une attaque en ciblant les failles présentes dans des versions spécifiques au lieu de taper au hasard.

Je ne sais pas à quel point le test est fiable mais si vous voulez voir de quoi il retourne, et que la collecte de ces informations par le système de test ne vous dérange pas, vous pouvez vous rendre sur cette page pour lancer le test avec votre configuration : Panopticlick

Windows Seven, Excel 2010, ODBC : le choc des générations

Il y a des fois où on fait des trucs bizarres (Dans Excel : Data => From Other Sources => From Data Connection Wizard => ODBC DSN => Excel Files ou MS Access Database) et on tombe sur des trucs encore plus bizarres :

Windows Seven, Excel 2010, ODBC : le choc des générations

Je plains les gens qui ont besoin de se servir de ça.
Au moins ça me remontera le moral quand je maudirai la boite de dialogue de preview de refactoring de VS2010, qui sauf miracle depuis la beta 2 devrait être comme celle de VS2008 : sans splitter...

Facebook et la sécurité

Je suis tombé sur un article de Pierre CARON (CERT-LEXSI) : Pourquoi Facebook a crucifié la sécurité (en français).
Cet article est plutôt facile à lire et l'auteur y donne son point de vue sur les raisons de certaines lacunes dans le fonctionnement de la plateforme, et les raisons pour lesquelles il pense que ces lacunes ne seront probablement jamais comblées.

Sujets abordés :

  • contrôle d'identité : on n'est jamais réellement certains que sous un profil se cache réellement la personne à qui il est censé appartenir, sauf si on en a la confirmation directe par celle-ci.
  • authentification forte : la plateforme est gratuite et devrait le rester si elle veut conserver son public, alors mettre en oeuvre un moyen d'authentification engendrant des coûts supplémentaires...
  • absence de modération : je n'ose imaginer les moyens à mettre en oeuvre pour réellement filtrer les contenus publiés sur Facebook.
  • l'utilisation de Facebook sur le lieu de travail : le blocage n'empêchera pas la fuite d'information si elle devait avoir lieu, au mieux il améliorera la productivité.

Vous aviez probablement déjà conscience de la plupart de ces points et ne partagez peut être pas l'avis de l'auteur sur tous, mais il est toujours bon d'avoir un autre point de vue.
Au moins pour tenter de convaincre ses relations que l'utilisation de ce genre de plateforme grand public (ou pas) n'est pas sans risque, juste après les avoir convaincus qu'il faut qu'ils utilisent l'outil en se disant que toute information qu'ils déposent dessus pourrait bien un jour échapper à leur contrôle.

Ceci dit, cela ne veut pas dire que les équipes techniques/sécurité de Facebook font du mauvais travail.
Ils ont quand même l'air de bien gérer les fléaux qui ciblent ce genre de plateforme énorme et grand public, je pense notamment au spam (du moins de mon point de vue, mais c'est peut être dû aux profils des gens avec qui je suis en relation).

 

Et je profite de ce premier post de 2010 pour vous souhaiter à tous une bonne année !

Channel 9 : Code Contracts et Pex pour .NET en FRANCAIS

Etonnant de voir passer un titre en français dans les flux Channel 9 et de découvrir une vidéo dans la même langue au bout du lien, ça mérite d'être signalé !

Il s'agit d'une petite présentation de Code Contracts et Pex par Manuel Fähndrich et Peli de Halleux :

Manuel Fahndrich et Peli de Halleux - Code Contracts et Pex pour .NET

Voilà, ceux qui préfèrent la langue de Molière à celle de Shakespeare peuvent aussi aller sur Channel 9 maintenant !

A quand des vidéos sur le Garbage Collector en français par Patrick Dussud ? :p

SQL Server et la gestion des espaces en fin de chaîne (ou 'test' = 'test ')

Il s'agit d'une petite chose qu'il vaut mieux garder en tête quand on développe sur/pour SQL Server, et probablement d'autres SGBD si j'en crois la raison avancée que nous verrons plus bas.
C'est particulièrement surprennant pour quelqu'un ayant l'habitude de travailler avec d'autres langages, comme C#, qui n'ont pas ce genre de comportement.

Les chaînes (typées varchar, nvarchar) contenant des espaces en fin de chaîne sont considérées égales à leurs équivalents ne les possédant pas, et ce sur pratiquement pour toutes les opérations reposant sur ou utilisant une comparaison à l'exception de la partie droite d'une clause LIKE.

 

Ainsi ce script affichera 'true' :

declare @Str nvarchar(256);
declare @StrLW nvarchar(256);

set @Str = N'test';
set @StrLW = N'test ';

select case when @Str = @StrLW
then N'true'
else N'false'
end

 

Celui-ci ne sortira qu'une entrée dans le jeu de résultats, et ce sera probablement la valeur de @Str :

create table test (txt nvarchar(256));
insert into test (txt) values (@Str);
insert into test (txt) values (@StrLW);
select distinct txt from test;

Celui-ci (identique au précédent si ce n'est l'inversion des ordres INSERT) sortira probablement la valeur de @StrLW :

create table test (txt nvarchar(256));
insert into test (txt) values (@StrLW);
insert into test (txt) values (@Str);
select distinct txt from test;

(Avec une vraie table et une requête plus poussée la valeur extraite dépendra probablement des indexes mis en oeuvre.)

 

La raison invoquée pour expliquer ce comportement pour le moins étrange est une compatibilité avec la norme SQL-92 : INF: How SQL Server Compares Strings with Trailing Spaces

SQL Server follows the ANSI/ISO SQL-92 specification (Section 8.2, <Comparison Predicate>, General rules #3) on how to compare strings with spaces. The ANSI standard requires padding for the character strings used in comparisons so that their lengths match before comparing them. The padding directly affects the semantics of WHERE and HAVING clause predicates and other Transact-SQL string comparisons. For example, Transact-SQL considers the strings 'abc' and 'abc ' to be equivalent for most comparison operations.
Source : INF: How SQL Server Compares Strings with Trailing Spaces

 

Concernant LIKE, je vous laisse exécuter les 2 scripts ci-dessous (les différences entre les 2 résident dans les types utilisés pour les variables et les chaînes littérales) et observer less résultats :

declare @Str nvarchar(256);
declare @StrLW nvarchar(256);

set @Str = N'test';
set @StrLW = N'test ';

select N'true' where @StrLW like @Str;
select N'true' where @Str like @StrLW;
select N'true' where @StrLW like N'test';
select N'true' where @Str like N'test ';

declare @Str varchar(256);
declare @StrLW varchar(256);

set @Str = 'test';
set @StrLW = 'test ';

select N'true' where @StrLW like @Str;
select N'true' where @Str like @StrLW;
select N'true' where @StrLW like 'test';
select N'true' where @Str like 'test ';

 

Ca peut ouvrir la voie à des casses-têtes plus ou moins graves...

Autant ce comportement ne me choque pas, et je dirais même que je m'y attendais, pour un type à largeur fixe (char, nchar) autant pour un type à longueur variable (varchar, nvarchar) je ne m'y attendais vraiment pas.

Donc essayons de retenir cette spécificité pour éviter de la redécouvrir dans la douleur la prochaine fois !

Webcast sur le nouveau système d'aide qui sortira avec Visual Studio 2010

Pour ceux qui se posent des questions à propos du nouveau système d'aide (déjà utilisable dans les versions beta de VS2010), une interview de Ryan Linton vous donnera peut être quelques réponses.
Cette vidéo (10 minutes, en anglais) est disponible est disponible sur Channel9 : Microsoft Help Viewer - New Help System in Visual Studio 2010

J'ai cru entendre parler de mon ami l'index en fin d'interview.

Attacher un debugger au démarrage d'un processus

Dans la série des astuces dont on n'a pas besoin tous les jours, et qu'on ne retrouve pas le jour J : attacher un debugger au démarrage d'un processus.

Vous en avez sans doute déjà entendu parlé au travers de l'outil Global Flags installé avec les Debugging Tools for Windows :

Debugging Tools for Windows - Global Flags

Ou alors vous avez entendu parlé de la clé de registre HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options (que Global Flags modifie).

Ca peut servir, surtout pour tout ce qui doit être analysé dès le démarrage mais ne peut être lancé directement depuis le debugger comme les services Windows.

Nommez vos variables de la même manière que votre premier-né

"You should name a variable using the same care with which you name a first-born child."
- James O. Coplien, Denmark   (foreword to Clean Code)

Je suis tombé sur cette citation via ce post : Name a variable like you name your first-born

C'est sans doute volontairement exagéré, mais justement ça souligne bien l'importance de nommer correctement une variable, ce qui n'est pas forcément une tâche aisée.
Il faut réussir à exprimer avec quelques mots la raison d'exister de la variable, tout en restant le plus concis possible afin de ne pas atteindre des longueurs trop importantes.

Au moment de la frappe initiale du code, utiliser une variable "i", "x", "y", "instance" (...) peut paraitre anodin, mais souvenez vous de la dernière fois que vous avez maintenu du code utilisant ce type de nommage...

Nommez vos variables en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse.

Designing Applications for High Performance

Je suis tombé aujourd'hui, via un post sur le blog ASKPERF, sur une série de 3 posts de Rick Vicik (Architect, Windows Server Performance Team et une des personnes derrière SQL Server).

Pas de .NET là dessous : on parle plus de manière générale et pour de la documentation liée aux exemples donnés vous pouvez directement aller dans la section Platform SDK de la MSDN ou dans la documentation du WDK.
Je n'ai pas forcément tout saisi mais de ma première lecture je sens que certaines informations exposées dans ces posts pourront me servir à terme, et certaines logiques en elles mêmes devraient être applicables côté .NET.

Voici les liens vers les posts en question, datant de 2008 :

Bref, avis aux motivés :-)
C'est relativement volumineux à lire, mais vous avez sans doute déjà connu plus massif (mais bien quand même) !
Et au passage ça me fait un petit pense-bête pour le jour J.

Avis à ceux qui en ont un sur la documentation technique MSDN (Visual Studio, .NET, Win32, etc)

Vous avez une chance de donner votre avis (en anglais) sur la documentation technique MSDN : A Chance to Influence the MSDN Visual Studio Documentation (Help System)

AES (Advanced Encryption Standard) expliqué en dessin "stickmen"

C'est chez Jeff Moser : A Stick Figure Guide to the Advanced Encryption Standard (AES)

J'ai trouvé la façon de le présenter plutôt originale et sympathique !

2 extraits pour donner un aperçu :

aes act 1 scene 11 triple des

aes act 3 scene 02 agreement

Il y a Base64 et Base64...

Vous connaissez tous ce fameux encodage dit "Base64", consistant à représenter des données binaires en utilisant un jeu de 64 caractères, en général définis comme étant composé de :

  • la plage de 'A' à 'Z' (26 caractères)
  • la plage de 'a' à 'z' (26 caractères)
  • la plage de '0' à '9' (10 caractères)
  • les symboles '+' et '/'

Un 65ème caractère, ne représentant pas une donnée, sert au padding en fin de chaîne : '='.

Les développeurs .NET l'utilisent souvent au travers de la méthode Convert.ToBase64String.

Du coup comme à chaque fois que des symboles sont utilisés en supplément de traditionnels caractères alphanumériques, c'est à dire des caractères susceptibles d'être interdit dans un système de fichier etc, une question se pose : tout le monde est censé utiliser ce jeu de caractères et seulement celui-là ?

Dans le cas de Base64 la réponse est non.
Il existe de manière "officielle" au moins 2 alphabets :

Base32 n'est guère mieux : Base 32 Encoding with Extended Hex Alphabet.

 

Pour les cas où la donnée d'origine doit être retrouvée ou ceux où la même chaîne doit être générée pour une entrée donnée, ça peut être problématique.
On peut citer comme exemple la synchronisation interprocessus de l'accès à une ressource (du genre système de fichier) au moyen d'un mutex : nous sommes limités en longueur de nom (MAX_PATH, voir CreateMutex) donc hors de question d'utiliser le chemin d'accès (un bon vieux hash SHA-2 du chemin fera donc l'affaire) et nous aurons besoin d'être certains que les 2 processus génèreront exactement la même chaîne pour un chemin d'accès donné.

Si nous choisissons Base64 pour encoder le hash nous aurons donc au minimum besoin de nous assurer que le même alphabet soit utilisé des 2 côtés, et ce de manière durable.
Peu de risque que l'implémentation de Convert.ToBase64String change mais je n'ai pas vu de garantie non plus.

Sinon, vu que dans le cas du mutex le besoin de compacter au maximum la longueur n'est pas forcément nécessaire (tant que nous ne dépassons pas les 260 caractères au total), un bon vieux Base16 (hex) ira très bien : SHA-512 = 64 bytes = 128 caractères (au lieu des 88 caractères obtenus avec Base64).

Si vous aussi vous suivez quelques flux "Most Recent Feedback" sur Connect...

... parce que vous aimez avoir une chance d'être au courant d'un bug avant de le prendre de plein fouet et que vous trouvez que c'est calme ces derniers temps, ce n'est pas normal.
Ce feedback sur Connect (sur Connect :p) peut vous intéresser.

Attribut PrincipalPermission : attention aux effets de "caspol.exe -s off"

Il n'est pas rare, lorsqu'on vient à parler de contrôle d'accès par rôle, de voir des solutions préconisées reposant sur l'attribut PrincipalPermission.
Le code du corps de la méthode suivante n'est censé s'exécuter que si l'utilisateur est membre du groupe "Administrators" :

[PrincipalPermission(SecurityAction.Demand, Role = "Administrators")]
public void RunSomethingAdministrative()
{
// ...
}
 

Problème : le comportement lié à cet attribut est impacté par la commande "caspol.exe -s off"

Attention : Mes connaissances sur le hosting du CLR ne me permettent pas de dire si un hôte (IIS etc) peut choisir de surcharger cette désactivation. Je ne peux donc pas affirmer que le problème a lieu pour un code hosté par IIS (ASP.NET, WCF, ...), si quelqu'un se sent le courage de tester... ;-)

 

 

caspol -s off ?

Toute personne ayant déjà joué avec le CAS (ou utiliser VSTO dans ses premières versions) connait le switch "-s" de l'outil caspol, qui permet de rendre le CAS inopérant pour les applications .NET lancées après exécution de la commande.

En .NET 1.x, cette commande pouvait être utilisée pour une désactivation d'une durée indéterminée, l'outil caspol terminant son exécution directement (d'où la présence de "-s on" pour réactiver la sécurité).

Avec .NET 2.0, cette commande a été revue afin de mieux affirmer que son existence est uniquement dédiée à des fins de test : à l'exécution caspol désactive la sécurité mais reste ouvert jusqu'à appui sur "Entrée", ce qui a pour effet la réactivation immédiate de la sécurité pour toute nouvelle application lancée par la suite.
Les applications lancées pendant la désactivation restent en mode désactivé, je suppose que la vérification de la présence du mutex se fait à l'initialisation du CLR, pas à l'évaluation des permissions (ce qui serait logique vu les précautions prises).

Malgré le fait que l'implémentation de cette fonctionnalité est moins dangereuse depuis .NET 2.0 car non permanente (enfin, normalement... Dans l'utilisation normale elle ne l'est pas, même en cas d'arrêt brutal de caspol), il n'en reste pas moins qu'elle est globale au système et non pas limitée à la session en cours (bref, sa portée est celle d'un mutex créé avec le préfixe "Global\", voir CreateMutex/CreateMutexEx).

 

 

En quoi cela nous concerne ? 

Tout d'abord, reprécisons bien une chose : même si ils ont la même finalité, il ne faut pas confondre l'attribut PrincipalPermission (classe PrincipalPermissionAttribute) et la classe PrincipalPermission.

L'attribut permet la spécification de manière déclarative :

[PrincipalPermission(SecurityAction.Demand, Role = "Administrators")]
public void RunSomethingAdministrative()
{
// ...
}

Alors que la classe PrincipalPermission sera utilisée de manière impérative :

public void RunSomethingAdministrative()
{
PrincipalPermission perm = new PrincipalPermission(
null, "Administrators");
perm.Demand();

// Do something
Thread.CurrentThread.Join(1000);
}

 

La première fois que j'ai eu à envisager d'utiliser cet attribut moi même (étonnamment, cette semaine), le fait que PrincipalPermissionAttribute et PrincipalPermission résident dans le namespace System.Security.Permissions m'a tout de suite fait penser à cette fameuse commande de caspol... et à la question "Si je l'exécute, plus aucune validation de rôle ?".

Dans un sens ce serait logique de ne plus avoir de barrière, c'est un peu le comportement attendu de "-s off" tout de même.
Mais dans les faits ce que j'attends (à titre personnel, allez savoir si j'ai raison ou pas) de PrincipalPermissionAttribute/PrincipalPermission est que leur validation soit toujours active, quoi qu'il arrive.

 

La documentation de caspol (dans sa version .NET 2.0) dit :

-s[ecurity] {on | off}
Turns code access security on or off. Specifying the -s off option does not disable role-based security.

Chouette ! Mais vérifions tout de même.

 

Prenons le code suivant :

internal interface IService
{
void RunSomethingAdministrative();
}

/// <summary>
/// Méthode déclarative
/// </summary>
internal class DeclarativePPService : IService
{
[PrincipalPermission(SecurityAction.Demand, Role = "Administrators")]
public void RunSomethingAdministrative()
{
// Do something
Thread.CurrentThread.Join(1000);
}
}

/// <summary>
/// Méthode impérative avec PrincipalPermission
/// </summary>
internal class ImperativePPService : IService
{
public void RunSomethingAdministrative()
{
PrincipalPermission perm = new PrincipalPermission(
null, "Administrators");
perm.Demand();

// Do something
Thread.CurrentThread.Join(1000);
}
}

/// <summary>
/// Méthode impérative avec levée manuelle d'une exception
/// </summary>
internal class ImperativeCustomService : IService
{
public void RunSomethingAdministrative()
{
this.EnsureAccessGranted();

// Do something
Thread.CurrentThread.Join(1000);
}

private void EnsureAccessGranted()
{
if (!Thread.CurrentPrincipal.IsInRole("Administrators"))
{
throw new AccessDeniedException("Access denied !");
}
}
}

J'ai pris volontairement le cas d'une situation à risque (en l'absence d'emprunt d'identitité/délégation, risque d'élévation de privilège en cas de défaut de validation correcte du rôle de l'appelant) du point de vue technique sur un système, pour lequel on aurait à priori plutôt opté directement pour un mode plus paranoïaque (vérification impérative) et surtout choisi un modèle en délégation (mais des fois ce n'est pas applicable).
Mais le cas d'un rôle métier est tout aussi problématique même si moins grave pour le système au premier abord (cependant on ne sait pas vraiment si à terme ce groupe ne finira pas par disposer de privilèges sur le système en lui même).

Si j'exécute le test suivant dans une situation normale, avec un compte non administrateur :

static void Main(string[] args)
{
Program.RunTest(new DeclarativePPService());
Program.RunTest(new ImperativePPService());
Program.RunTest(new ImperativeCustomService());
}

private static void RunTest(IService implementation)
{
String message = null;

try
{
implementation.RunSomethingAdministrative();
message = "ACCESS GRANTED !";
}
catch (SecurityException ex)
{
message = String.Format("Access denied ! ({0})", ex.Message);
}
catch (AccessDeniedException ex)
{
message = String.Format("Access denied ! ({0})", ex.Message);
}

Console.WriteLine("{0,-23} : {1}",
implementation.GetType().Name, message);
}

Nous obtenons bien, comme prévu :

DeclarativePPService    : Failed ! (Request for principal permission failed.)
ImperativePPService : Failed ! (Request for principal permission failed.)
ImperativeCustomService : Failed ! (Access denied !)

Relancé après désactivation du CAS :

DeclarativePPService    : SUCCESS !
ImperativePPService : Failed ! (Request for principal permission failed.)
ImperativeCustomService : Failed ! (Access denied !)

La vérification par rôle reste bien active dans son mode impératif, pas dans le mode déclaratif.

 

 

Ca peut être bon à retenir, au cas où...

En gros, sur cette permission qualifiable de "non-CAS", nous sommes un peu entre 2 mondes : désactiver le CAS désactive seulement le mode déclaratif, alors que pour les autres permissions la désactivation porte à la fois sur le mode déclaratif et le mode impératif. 

Bref, pas forcément un gros problème vu le peu d'utilisation qui est normalement fait de "caspol -s", mais il serait quand même bon (à mon avis) que les personnes intervenant sur des problèmes de production soient conscientes de cet effet qui ne vient pas forcément à l'esprit immédiatement.
Les conséquences pourraient être désastreuses dans le contexte d'un système fonctionnant non pas par emprunt d'identité/délégation mais sur le modèle "trusted subsystem" ("sous -système approuvé" / "sous-système de confiance").

 

Avec .NET 4.0 la situation devrait être différente vu les changements intervenant autour du CAS. Voir Security Changes in the .NET Framework 4 et Caspol.exe (Code Access Security Policy Tool).

La citation du weekend

Je viens de tomber sur ça :

"Documentation is like sex; when it's good, it's very, very good, and when it's bad, it's better than nothing."
Dick Brandon

J'adore :p

var, var, var... marre, marre, marre...

Au risque de passer pour un vieux c** : pourquoi mettre du var partout ?
Je parle de l'utilisation avec autre chose que des types anonymes bien sûr.
Je parle de l'utilisation avec des vrais types, issus de la BCL ou non.

var str = "bla";
var i = 1;
var value = instance.Value;

On sent tout de suite la puissance du truc là, une vraie révolution.
On risque surtout de bien sentir passer la maintenance du code.

 

 

C'est énervant à la lecture

var i = 1;
var value = instance.Value;

Exercice : Au premier coup d'oeil, donner le type de "i".
Vous savez que le type sous un littéral de ce genre est int ? Et vos collègues ?

Exercice : Au premier coup d'oeil, donner le type de "value".
Là vous savez ? Recommencez la semaine prochaine.

 

C'est particulièrement énervant quand c'est utilisé dans des articles : si vous avez le malheur de lire l'article sans avoir sous la main votre documentation et/ou une connexion à internet, vous vous retrouvez avec une question bien sympathique : "mais c'est quoi le type sur lequel une méthode DoSomethingReallyCool est disponible ?"

var value = instance.Value;
...
value.DoSomethingReallyCool();


Ca m'est arrivé ce matin...

 

 

C'est risqué vis-à-vis des changements du code externe

En dehors du côté énervant à la lecture, un autre problème se pose : la détection des changements potentiellement critiques dans le code utilisé.

Imaginons que notre propriété Value retourne une valeur Int32.
Après récupération de cette valeur, nous la transmettons à un autre code/système qui pour une raison ou une autre ne la demande pas sous forme fortement typée Int32 mais plutôt Object, Byte[], ...

public class OtherClass
{
public void DoSomethingWith32Bits(Object value)
{
// ...
}
}

Nous pouvons utiliser au choix l'un de ces 2 codes (oui, je sais : ils sont bidons et moches mais ça suffira) : Method1 utilise un vrai type, Method2 laisse le compilateur inférer le type :

private void Method1()
{
OneClass instance = new OneClass();
OtherClass otherInstance = new OtherClass();
Int32 value = instance.Value;
otherInstance.DoSomethingWith32Bits(value);
}

private void Method2()
{
OneClass instance = new OneClass();
OtherClass otherInstance = new OtherClass();
var value = instance.Value;
otherInstance.DoSomethingWith32Bits(value);
}

Plus tard, le code de OneClass est changé : Value renvoie maintenant un Int64.
Oui, bien sûr, en théorie ce n'est pas censé arriver... Mais il y a la théorie et la pratique. Surtout si nous ne sommes pas maître du code de OneClass.

Avec le code typé normalement (Method1), notre ami le compilateur nous prévient à la compilation suivante : "Cannot implicitly convert type 'long' to 'int'. An explicit conversion exists (are you missing a cast?)"
Nous pouvons aller nous expliquer avec le propriétaire de OneClass, ou apporter les corrections nécessaires si c'était prévu.

Avec le code typé n'importe comment (Method2), nous venons de basculer automatiquement d'un entier 32 bits à un entier 64 bits.
Pratique ! J'espère juste pour nous que le code/système derrière OtherClass va lui aussi apprécier le changement, dans le cas contraire nous risquons quelques blagues particulièrement drôles :

  • Crash à la première exécution du code
    Peut être en prod si le code n'est pas couvert par des tests systématiques. Car si cette fonctionnalité n'est pas censée être impactée par l'opération de maintenance, elle ne sera peut être pas testée avant la mise en prod.
    • Soft : nous avons de la chance, c'est juste l'application.
    • Hard : OtherClass utilise en interne du code non managé et nous n'avons vraiment pas de chance, notre valeur est utilisée une fois l'exécution passée en kernel mode.
      Un bon BSOD nous fera bien voir par nos utilisateurs (ou plus si notre code est hébergé sur un système non dédié).
  • Nous nous apercevons au bout de plusieurs jours/semaines/mois que nous avons corrompus des données

 

On ne parle ici que d'un simple changement de type de valeur, mais avec l'utilisation de var il est de manière générale possible de basculer d'un type à un autre sans s'en rendre compte du moment que ce dernier expose les mêmes membres que ceux utilisés par le code spécifiant var comme "type".

 

Prenons le code suivant :

public class OneClass
{
public NiceClass Value
{
get
{
return new NiceClass();
}
}
}

public class NiceClass
{
public void DoSomethingNice()
{
Console.WriteLine("Hello !");
}
}

class Program
{
static void Main(string[] args)
{
OneClass instance = new OneClass();
var niceInstance = instance.Value;
niceInstance.DoSomethingNice();
}
}

Sortie : "Hello !"

Ajoutons le code de FakeNiceClass
public class FakeNiceClass
{
public void DoSomethingNice()
{
Console.WriteLine("BOOM !");
}
}

Et changeons juste le code de OneClass :
public class OneClass
{
//public NiceClass Value
//{
// get
// {
// return new NiceClass();
// }
//}

public FakeNiceClass Value
{
get
{
return new FakeNiceClass();
}
}
}

Ca compile sans problème, et la sortie est "BOOM !".

La version de Main avec vrai typage aurait nécessité, pour passer la phase de compilation, de mettre en place un opérateur de conversion implicite :

  • sur FakeNiceClass, et c'est quand même le code de NiceClass.DoSomethingNice qui aurait été exécuté, pas celui de FakeNiceClass.DoSomethingNice.
  • sur NiceClass, et c'est le code de NiceClass.DoSomethingNice qui aurait été exécuté, sauf décision explicite dans NiceClass.DoSomethingNice de déléguer l'exécution à FakeNiceClass.DoSomethingNice persistée lors de la conversion.

Déjà plus compliqué à réussir de manière non intentionnelle...

Posted: samedi 10 octobre 2009 22:02 par coq | 29 commentaire(s)
Classé sous : ,
Point d'arrêt spécifique à une instance : Make Object ID peut vous aider

En parcourant la liste des tips que John Robbins dissémine dans ses articles Bugslayer pour MSDN Magazine, je suis tombé sur l'astuce numéro 79, issue de l'article Wait Chain Traversal du numéro de juillet 2007 (celui là, je ne l'ai sans doute pas lu jusqu'au bout) :

"Tip 79 Gregg Miskelly came up with a brilliant debugging trick on his blog. In managed code, you don’t have the address of an object, so setting a per instance breakpoint is almost impossible. However, Gregg points out that if you do the cool Make Object Id trick on the instance, you can set a per instance breakpoint by setting a conditional breakpoint to this == 1#. That’s one I could have used a million times over!"

Quand on a besoin d'avoir un breakpoint pour une/des instance(s) spécifique(s), on peut éventuellement utiliser une propriété (ou champs) possédant une valeur unique et immuable.
Mais quand ce type d'information n'est pas disponible sur le type ciblé (ou que l'invocation d'une telle propriété entrainerait des changements d'état polluant le debugage), on ne peut pas le faire.
C'est là qu'intervient l'idée d'utiliser l'Object ID (voir présentation par Cyril ou John) et de l'utiliser en tant qu'expression évaluée pour le point d'arrêt conditionnel : "this == 1#"

L'inconvénient est que ces Object ID ne survivent pas d'une exécution à l'autre du programme débuggé, mais ce n'est guère différent de l'utilisation d'une valeur unique de l'instance si cette valeur n'est pas stable entre 2 exécutions.

Mais pourquoi je n'y pense pas quand j'en ai besoin ?!

Avis aux utilisateurs de NDepend : donnez votre avis sur le futur de NDepend

Pour les utilisateurs de NDepend qui ne suivraient pas le blog de Patrick Smacchia : une enquête a été ouverte afin de vous permettre de, notamment, faire part de vos attentes pour les prochaines versions de ce sympatique outil.

Le questionnaire à compléter est lié sur le post de Patrick : Influence the future of NDepend

Plus de Messages Page suivante »


Les 10 derniers blogs postés

- TechDays Paris 2010 : Plan de migration vers SharePoint 2010 par Blog Technique de Romelard Fabrice le il y a 2 heures et 15 minutes

- TechDays Paris 2010 : La pleinière du second jour par Blog Technique de Romelard Fabrice le il y a 3 heures et 20 minutes

- Visual Studio 2010 and .NET Framework 4 Release Candidate now available par Matthieu MEZIL le il y a 6 heures et 25 minutes

- Création d’une base de donnée sous SQL Azure par Le Blog (Vert) d'Arnaud JUND le il y a 7 heures et 22 minutes

- TechDays Paris 2010 : Les Services d’applications dans SharePoint 2010 par Blog Technique de Romelard Fabrice le il y a 17 heures et 21 minutes

- TechDays Paris 2010 : La GED et SharePoint 2010 par Blog Technique de Romelard Fabrice le il y a 21 heures et 20 minutes

- TechDays Paris 2010 : SharePoint 2010 et Les réseaux sociaux par Blog Technique de Romelard Fabrice le il y a 22 heures et 33 minutes

- TechDays Paris 2010 : SharePoint 2010 – Description et nouveautés par Blog Technique de Romelard Fabrice le il y a 23 heures et 40 minutes

- TechDays Paris 2010 : Pleinière Lundi par Blog Technique de Romelard Fabrice le il y a 23 heures et 44 minutes

- [Techdays 2010] #02 - Nouveautés de SharePoint 2010 par Le petit blog de Pierre / Pierre's little blog le 02-08-2010, 13:52