322 votes

Quel est le cas le plus étrange que vous ayez vu en C# ou en .NET ?

Je collectionne quelques cas particuliers et casse-tête et j'aimerais toujours en savoir plus. La page ne couvre que les éléments du langage C#, mais je trouve également que les éléments de base de .NET sont intéressants. Par exemple, en voici une qui ne figure pas sur la page, mais que je trouve incroyable :

string x = new string(new char[0]);
string y = new string(new char[0]);
Console.WriteLine(object.ReferenceEquals(x, y));

Je m'attendrais à ce qu'il s'imprime False - après tout, "new" (avec un type de référence) toujours crée un nouvel objet, n'est-ce pas ? Les spécifications de C# et de l'interface de programmation indiquent que c'est le cas. Eh bien, pas dans ce cas particulier. Elle affiche True, et l'a fait sur toutes les versions du framework avec lesquelles je l'ai testée. (Je ne l'ai pas testé sur Mono, il est vrai...)

Pour être clair, il s'agit seulement d'un exemple du genre de chose que je recherche - je ne cherchais pas particulièrement à discuter/expliquer cette bizarrerie. (Ce n'est pas la même chose que l'interfaçage normal de chaînes de caractères ; en particulier, l'interfaçage de chaînes de caractères ne se produit pas normalement lorsqu'un constructeur est appelé). Je demandais en fait un comportement étrange similaire.

D'autres perles se cachent-elles dans les environs ?

394voto

Marc Gravell Points 482669

Je crois que je vous ai déjà montré celui-ci, mais j'aime l'amusement ici - il a fallu un peu de débogage pour le trouver ! (le code original était évidemment plus complexe et subtil...)

    static void Foo<T>() where T : new()
    {
        T t = new T();
        Console.WriteLine(t.ToString()); // works fine
        Console.WriteLine(t.GetHashCode()); // works fine
        Console.WriteLine(t.Equals(t)); // works fine

        // so it looks like an object and smells like an object...

        // but this throws a NullReferenceException...
        Console.WriteLine(t.GetType());
    }

Qu'est-ce que T...

Réponse : tous Nullable<T> - tels que int? . Toutes les méthodes sont surchargées, à l'exception de GetType() qui ne peut pas l'être ; elle est donc castée (boxée) en objet (et donc en null) pour appeler object.GetType()... qui appelle null ;-p


Mise à jour : l'affaire se corse... Ayende Rahien a jeté un défi similaire sur son blog mais avec un where T : class, new() :

private static void Main() {
    CanThisHappen<MyFunnyType>();
}

public static void CanThisHappen<T>() where T : class, new() {
    var instance = new T(); // new() on a ref-type; should be non-null, then
    Debug.Assert(instance != null, "How did we break the CLR?");
}

Mais il peut être vaincu ! En utilisant la même indirection que celle utilisée pour des choses comme le remoting ; attention - ce qui suit est le mal à l'état pur :

class MyFunnyProxyAttribute : ProxyAttribute {
    public override MarshalByRefObject CreateInstance(Type serverType) {
        return null;
    }
}
[MyFunnyProxy]
class MyFunnyType : ContextBoundObject { }

Une fois ce dispositif en place, le new() est redirigé vers le proxy ( MyFunnyProxyAttribute ), qui renvoie null . Maintenant, va te laver les yeux !

216voto

Samuel Kim Points 3107

L'arrondi des banquiers.

Il ne s'agit pas vraiment d'un bug ou d'un dysfonctionnement du compilateur, mais certainement d'un cas particulier...

Le .Net Framework utilise un système d'arrondi connu sous le nom de Banker's Rounding (arrondi du banquier).

Dans l'arrondi bancaire, les chiffres de 0,5 sont arrondis au nombre pair le plus proche.

Math.Round(-0.5) == 0
Math.Round(0.5) == 0
Math.Round(1.5) == 2
Math.Round(2.5) == 2
etc...

Cela peut entraîner des bogues inattendus dans les calculs financiers basés sur l'arrondi à la moitié supérieure, plus connu.

Il en va de même pour Visual Basic.

176voto

Greg Beech Points 55270

Que fera cette fonction si elle est appelée en tant que Rec(0) (pas sous le débogueur) ?

static void Rec(int i)
{
    Console.WriteLine(i);
    if (i < int.MaxValue)
    {
        Rec(i + 1);
    }
}

Réponse :

  • Sur une JIT 32 bits, il devrait en résulter une StackOverflowException.
  • En mode JIT 64 bits, tous les nombres devraient être imprimés dans int.MaxValue.

Cela s'explique par le fait que le compilateur JIT 64 bits applique l'optimisation des appels de queue alors que la JIT 32 bits ne le fait pas.

Malheureusement, je n'ai pas de machine 64 bits sous la main pour le vérifier, mais la méthode remplit toutes les conditions pour l'optimisation de l'appel de queue. Si quelqu'un en a une, je serais intéressé de voir si c'est vrai.

111voto

Omer Mor Points 3658

Attribuer ceci !


C'est une question que j'aime poser lors des fêtes (et c'est probablement la raison pour laquelle je ne suis plus invitée) :

Pouvez-vous compiler le morceau de code suivant ?

    public void Foo()
    {
        this = new Teaser();
    }

Une tricherie facile pourrait être :

string cheat = @"
    public void Foo()
    {
        this = new Teaser();
    }
";

Mais la vraie solution est la suivante :

public struct Teaser
{
    public void Foo()
    {
        this = new Teaser();
    }
}

C'est donc un fait peu connu que les types de valeurs (structs) peuvent réaffecter leur this variable.

100voto

Jarek Kardas Points 6956

Il y a quelques années, alors que nous travaillions sur un programme de fidélisation, nous avons rencontré un problème concernant le nombre de points attribués aux clients. Le problème était lié à la conversion des doubles en int.

Dans le code ci-dessous :

double d = 13.6;

int i1 = Convert.ToInt32(d);
int i2 = (int)d;

est-ce que i1 == i2 ?

Il s'avère que i1 != i2. En raison des différentes politiques d'arrondi de l'opérateur Convert et de l'opérateur cast, les valeurs réelles sont les suivantes :

i1 == 14
i2 == 13

Il est toujours préférable d'appeler Math.Ceiling() ou Math.Floor() (ou Math.Round avec MidpointRounding qui répond à nos besoins).

int i1 = Convert.ToInt32( Math.Ceiling(d) );
int i2 = (int) Math.Ceiling(d);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X