IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

VB.NET Discussion :

Cachez ce pointeur que je ne saurais voir : copie ou référence ?


Sujet :

VB.NET

  1. #1
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    934
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 934
    Points : 1 274
    Points
    1 274
    Par défaut Cachez ce pointeur que je ne saurais voir : copie ou référence ?
    Bonjour,

    Un des inconvénients de VB.NET est qu'on a parfois du mal à déterminer si on manipule une copie de la valeur d'un élément ou une référence vers cet élément.

    Très concrètement : j'ai une petite classe qui permet de travailler avec des bitmaps, en accédant aux données brutes dans un tableau :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    Public Class FastBitmap
        Public Bits As Int32()
        Public Height As Integer
        Public Width As Integer
     
        Private LockBmp As Bitmap
        Private BitsHandle As System.Runtime.InteropServices.GCHandle
     
        'Création à partir d'une image existante
        Public Sub New(file As String, Inverser As Boolean)
            Dim i As Integer, j As Integer
            Dim index As Integer, indexY As Integer
            Dim Bytes(4) As Byte
            Dim tmpBmp As Bitmap
            tmpBmp = CType(Image.FromFile(file), Bitmap)
            Width = tmpBmp.Width
            Height = tmpBmp.Height
            Bits = New Int32(Width * Height - 1) {}
            BitsHandle = System.Runtime.InteropServices.GCHandle.Alloc(Bits, System.Runtime.InteropServices.GCHandleType.Pinned)
            LockBmp = New Bitmap(Width, Height, Width * 4, System.Drawing.Imaging.PixelFormat.Format32bppPArgb, BitsHandle.AddrOfPinnedObject())
            Graphics.FromImage(LockBmp).DrawImage(tmpBmp, 0, 0) 'C'est un peu bourrin, mais fonctionne de façon sûr quel que soit le format d'image du fichier file
            tmpBmp.Dispose()
            'Maintenant, Bits(x + y * Width) contient la couleur ARGB sur 32 bits du pixel (x,y)
            'Chaque élement de Bits() est un Int32 dont on peut lire les 4 octets séparéments : Bytes(0)=B Bytes(1)=G Bytes(2)=R Bytes(3)=A 
            'Conversion niveau de gris :
            If Inverser Then
                For i = 0 To Height - 1
                    indexY = i * Width
                    For j = 0 To Width - 1
                        index = indexY + j
                        Bytes = BitConverter.GetBytes(Bits(index))
                        Bits(index) = FastBitmap_MAX_VALUE - 114000 * Bytes(0) - 587000 * Bytes(1) - 299000 * Bytes(2)
                    Next
                Next
            Else
                For i = 0 To Height - 1
                    indexY = i * Width
                    For j = 0 To Width - 1
                        index = indexY + j
                        Bytes = BitConverter.GetBytes(Bits(index))
                        Bits(index) = 114000 * Bytes(0) + 587000 * Bytes(1) + 299000 * Bytes(2)
                    Next
                Next
            End If
            'Maintenant, Bits(x + y * Width) contient la valeur de "Z" (niveau de gris) du pixel (x,y) sous forme de nombre entier compris entre 0 et FastBitmap_MAX_VALUE = 255000000
        End Sub
     
        'Création d'une image "vierge"
        Public Sub New(W As Integer, H As Integer)
            Width = W
            Height = H
            Bits = New Int32(Width * Height - 1) {}
            BitsHandle = System.Runtime.InteropServices.GCHandle.Alloc(Bits, System.Runtime.InteropServices.GCHandleType.Pinned)
            LockBmp = New Bitmap(Width, Height, Width * 4, System.Drawing.Imaging.PixelFormat.Format32bppPArgb, BitsHandle.AddrOfPinnedObject())
        End Sub
     
        'Retourne un objet graphics permettant de dessiner dans l'image
        Public Function GetGraphics(Optional sMode As Drawing2D.SmoothingMode = Drawing2D.SmoothingMode.None) As Graphics
            Dim g As Graphics
            g = Graphics.FromImage(LockBmp)
            g.SmoothingMode = sMode
            'Le SmoothingMode "none" est intéressant, il permet de dessiner avec une "fausse couleur" ARGB calculée pour correspondre à une valeur de Z précise de 0 à FastBitmap_MAX_VALUE
            Return g
        End Function
     
        Public Function GetFalseColor(Z As Int32, Optional Inverser As Boolean = False) As Color
            If Inverser Then Z = FastBitmap_MAX_VALUE - Z
            Return Color.FromArgb(Z)
        End Function
     
        Public Function GetFalseColor(ZPourcent As Double, Optional Inverser As Boolean = False) As Color
            Return GetFalseColor(CInt(ZPourcent * FastBitmap_MAX_VALUE), Inverser)
        End Function
     
        Public Sub Dispose()
            LockBmp.Dispose()
            BitsHandle.Free()
        End Sub
    End Class
    J'utilise ensuite cette classe dans mon code, et quand je fait cela :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    Public Sub AddForme3D_GetExtrusionZFZ_Lithophanie(NbPointsXY As Integer, NbPointsZ As Integer,
                                                          file As String, CentreX As Double, CentreY As Double, ZBase As Double, ZoomXY_Value As List(Of Double), ZoomXY_Z As List(Of Double),
                                                          Amplitude As Double, ByRef Base As sPoly2D, ByRef Sommet As sPoly2D,
                                                          FaceEXT As Boolean, Inverser As Boolean, Couleur As Color)
    '...
            Dim Bits As Int32()
            Dim Height As Integer
            Dim Width As Integer
            Dim fBmp As New FastBitmap(file, Inverser)
            Height = fBmp.Height
            Width = fBmp.Width
            Bits = fBmp.Bits
            'Bits(x + y * Width) est beaucoup plus rapide que GetPixel() avec un bitmap classique
    '...
    'Le reste du code utilise fortement Bits(), je ne veut pas faire à chaque fois un accès du genre fBmp.Bits()
    '...
            fBmp.Dispose()
    End Sub
    je ne sais pas si la ligne "Bits = fBmp.Bits" créé une copie du tableau "Bits" de ma classe fBmp, ou bien si c'est une référence.

    Autrement dit, est-ce que mon tableau "Dim Bits As Int32()" est copié ou bien est-ce que le tableau Bits est en fait un pointeur ? (en c++ "classique", le nom d'un tableau est un pointeur)

    En VB.NET les objets sont des pointeurs, une affectation avec le signe "=" ne fait que copier la référence, pour obtenir une vraie copie il faut écrire soit même dans son objet une fonction .Clone() ou .GetCopy() et y affecter le résultat.
    En revanche, dans VB.NET, avec les types de données simples (entiers, flottants, chaines de caractères), une affectation avec le signe "=" réalise une copie de la valeur ; pour avoir une référence il faut utiliser le mot clef ByRef si c'est un paramètre d'une fonction et dans les autres cas de figure ruser autrement.

    C'est un des aspects de VB.NET qui ne me plait pas
    Autant ça ne me dérange pas de trop qu'il n'y ai pas en VB la distinction entre "=" et "==", le "ByVal" par défaut pour les types de données classiques et le "ByRef" par défaut pour les objets est emmerdant.
    Par exemple dans une fonction de calcul quand on travaille avec des angles ou des coordonnées X,Y,Z qui sont des entiers ou des flottants et en même temps des objets "points" ou "polygone" c'est vite la migraine
    Je préférerait avoir deux affectations distinctes et explicites pour les copies de valeurs d'une part et pour les références d'autre part ; ça serait peut être un peu plus lourd mais plus propre et plus lisible.

    Pour ne pas remettre en cause la syntaxe du langage, ça serait pas mal un mode d'affichage dans l'IDE où le signe "=" aurait une couleur différente en fonction de son sens

    Merci

    A bientôt
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  2. #2
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    934
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 934
    Points : 1 274
    Points
    1 274
    Par défaut
    J'ai fait un petit test :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
            Dim Bits As Int32()
            Dim Height As Integer
            Dim Width As Integer
            Dim fBmp As New FastBitmap(file, Inverser)
            Height = fBmp.Height
            Width = fBmp.Width
            Bits = fBmp.Bits
            'Bits(x + y * Width) est beaucoup plus rapide que GetPixel() avec un bitmap classique
     
            Debug.Print("Avant : " & fBmp.Bits(0).ToString)
            Bits(0) = Bits(0) + 1
            Debug.Print("Après : " & fBmp.Bits(0).ToString)
            Bits(0) = Bits(0) - 1
    Le résultat dans la sortie déboguer :

    Avant : 0
    Après : 1

    Bon alors un tableau est bien manipulé par référence

    A bientôt
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  3. #3
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    934
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 934
    Points : 1 274
    Points
    1 274
    Par défaut
    La classe FastBitmap avec implémentation IDisposable :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    Public Class FastBitmap
        Implements IDisposable
        Protected disposed As Boolean = False
     
        Public Bits As Int32()
        Public Height As Integer
        Public Width As Integer
     
        Private LockBmp As Bitmap
        Private BitsHandle As System.Runtime.InteropServices.GCHandle
     
        'Création à partir d'une image existante
        Public Sub New(file As String, Inverser As Boolean)
            Dim i As Integer, j As Integer
            Dim index As Integer, indexY As Integer
            Dim Bytes(4) As Byte
            Dim tmpBmp As Bitmap
            tmpBmp = CType(Image.FromFile(file), Bitmap)
            Width = tmpBmp.Width
            Height = tmpBmp.Height
            Bits = New Int32(Width * Height - 1) {}
            BitsHandle = System.Runtime.InteropServices.GCHandle.Alloc(Bits, System.Runtime.InteropServices.GCHandleType.Pinned)
            LockBmp = New Bitmap(Width, Height, Width * 4, System.Drawing.Imaging.PixelFormat.Format32bppPArgb, BitsHandle.AddrOfPinnedObject())
            Graphics.FromImage(LockBmp).DrawImage(tmpBmp, 0, 0) 'C'est un peu bourrin, mais fonctionne de façon sûr quel que soit le format d'image du fichier file
            tmpBmp.Dispose()
            'Maintenant, Bits(x + y * Width) contient la couleur ARGB sur 32 bits du pixel (x,y)
            'Chaque élement de Bits() est un Int32 dont on peut lire les 4 octets séparéments : Bytes(0)=B Bytes(1)=G Bytes(2)=R Bytes(3)=A 
            'Conversion niveau de gris :
            If Inverser Then
                For i = 0 To Height - 1
                    indexY = i * Width
                    For j = 0 To Width - 1
                        index = indexY + j
                        Bytes = BitConverter.GetBytes(Bits(index))
                        Bits(index) = FastBitmap_MAX_VALUE - 114000 * Bytes(0) - 587000 * Bytes(1) - 299000 * Bytes(2)
                    Next
                Next
            Else
                For i = 0 To Height - 1
                    indexY = i * Width
                    For j = 0 To Width - 1
                        index = indexY + j
                        Bytes = BitConverter.GetBytes(Bits(index))
                        Bits(index) = 114000 * Bytes(0) + 587000 * Bytes(1) + 299000 * Bytes(2)
                    Next
                Next
            End If
            'Maintenant, Bits(x + y * Width) contient la valeur de "Z" (niveau de gris) du pixel (x,y) sous forme de nombre entier compris entre 0 et FastBitmap_MAX_VALUE = 255000000
        End Sub
     
        'Création d'une image "vierge"
        Public Sub New(W As Integer, H As Integer)
            Width = W
            Height = H
            Bits = New Int32(Width * Height - 1) {}
            BitsHandle = System.Runtime.InteropServices.GCHandle.Alloc(Bits, System.Runtime.InteropServices.GCHandleType.Pinned)
            LockBmp = New Bitmap(Width, Height, Width * 4, System.Drawing.Imaging.PixelFormat.Format32bppPArgb, BitsHandle.AddrOfPinnedObject())
        End Sub
     
        'Retourne un objet graphics permettant de dessiner dans l'image
        Public Function GetGraphics(Optional sMode As Drawing2D.SmoothingMode = Drawing2D.SmoothingMode.None) As Graphics
            Dim g As Graphics
            g = Graphics.FromImage(LockBmp)
            g.SmoothingMode = sMode
            'Le SmoothingMode "none" est intéressant, il permet de dessiner avec une "fausse couleur" ARGB calculée pour correspondre à une valeur de Z précise de 0 à FastBitmap_MAX_VALUE
            Return g
        End Function
     
        Public Function GetFalseColor(Z As Int32, Optional Inverser As Boolean = False) As Color
            If Inverser Then Z = FastBitmap_MAX_VALUE - Z
            Return Color.FromArgb(Z)
        End Function
     
        Public Function GetFalseColor(ZPourcent As Double, Optional Inverser As Boolean = False) As Color
            Return GetFalseColor(CInt(ZPourcent * FastBitmap_MAX_VALUE), Inverser)
        End Function
     
        Public Sub Inverser()
            Dim i As Integer, j As Integer, indexY As Integer, index As Integer
            For i = 0 To Height - 1
                indexY = i * Width
                For j = 0 To Width - 1
                    index = indexY + j
                    Bits(index) = FastBitmap_MAX_VALUE - Bits(index)
                Next
            Next
        End Sub
     
        Public Sub Clear(Optional Z As Int32 = 0)
            Dim i As Integer, j As Integer, indexY As Integer, index As Integer
            For i = 0 To Height - 1
                indexY = i * Width
                For j = 0 To Width - 1
                    index = indexY + j
                    Bits(index) = Z
                Next
            Next
        End Sub
     
        Protected Overridable Sub Dispose(ByVal disposing As Boolean)
            If Not disposed Then
                If disposing Then
                    'Code to free managed resources.
                End If
                'Code to free unmanaged resources.
                LockBmp.Dispose()
                BitsHandle.Free()
                Debug.Print("FastBitmap.Dispose()")
            End If
            disposed = True
        End Sub
        Public Sub Dispose() Implements IDisposable.Dispose
            Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub
        Protected Overrides Sub Finalize()
            Dispose(False)
            MyBase.Finalize()
        End Sub
    End Class
    Plus d'infos sur la gestion de la mémoire managée et non managée ici : https://docs.microsoft.com/fr-fr/dot...-and-destroyed
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  4. #4
    Membre émérite Avatar de Phil Rob
    Homme Profil pro
    Retraité
    Inscrit en
    Novembre 2013
    Messages
    1 613
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Novembre 2013
    Messages : 1 613
    Points : 2 865
    Points
    2 865
    Par défaut
    Bonsoir,

    De manière ordinaire, on peut affirmer que ce qui est instancié (avec New) et les tableaux se manipulent par pointeur ou référence, (la nuance qui est importante en C++, pas d'intérêt en VB ni en C#, je crois ...).

    Savoir si la manipulation qu'on envisage se fera sur la valeur elle-même ou sur une copie de cette valeur, peut-être important dans bien des cas et faire un test (comme tu viens de faire) est toujours une sage précaution.
    Il est possible de changer la situation par l'usage approprié des "vitamines" ByVal et ByRef dans les déclarations des fonctions et des procédures. En fait, ces ByVal et ByRef ne devraient jamais être omis. Mais attention, passer un tableau avec ByVal ne changera pas le fait qu'un tableau "est toujours" un pointeur.

    Voici quelques considérations sur la question : ByValByRef.pdf, ça peut servir ...

  5. #5
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    934
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 934
    Points : 1 274
    Points
    1 274
    Par défaut
    Merci pour le PDF, qui rassemble des infos de base utiles

    Petite particularité avec les tableaux en VB.NET

    Le code suivant :

    Génère un tableau avec n+1 élements : de TabP(0) à TabP(n)

    Cet élément "en trop" pose problème avec les fonctions qui lisent un tableau en totalité, comme graphics.AddPolygon(TabP)

    pour avoir un tableau de 'n' elements, allant de 0 à n-1 il faut faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Dim TabP(n-1) As PointF
    en effet, dans Dim TabP(n), le paramètre 'n' n'est pas le nombre d'élements, mais la borne supérieure, la borne inférieure étant par défaut 0.

    Dim Tab(n) as xxx est équivalent à Dim Tab(0 to n) as xxx donc n+1 éléments.
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  6. #6
    Membre émérite Avatar de Phil Rob
    Homme Profil pro
    Retraité
    Inscrit en
    Novembre 2013
    Messages
    1 613
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Novembre 2013
    Messages : 1 613
    Points : 2 865
    Points
    2 865
    Par défaut
    Tu as raison, c'est une particularité de VB.Net. Le(s) paramètre(s) de l'instruction DIM est l'indice maximal permis (et non le nombre d'éléments).
    Tu as raison aussi pour le pdf : c'est un extrait d'un cours destiné à des débutants.
    Bonne soirée...

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 9
    Dernier message: 12/06/2021, 05h14
  2. Pointeurs : que contiennent-ils ?
    Par gnulix dans le forum Débuter
    Réponses: 3
    Dernier message: 05/03/2008, 17h04
  3. Ne voire que certaines extensions et masquer les autres
    Par Furius dans le forum Balisage (X)HTML et validation W3C
    Réponses: 1
    Dernier message: 04/12/2005, 23h04
  4. comment savoir ce que fait mon pointeur??
    Par elekis dans le forum C++
    Réponses: 9
    Dernier message: 30/11/2004, 12h42
  5. Réponses: 8
    Dernier message: 26/08/2004, 18h59

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo