Je travaille avec le ref
et je ne comprends pas clairement "Est-ce comme un pointeur comme en C/C++ ou c'est comme une référence en C++?"
Pourquoi ai-je posé une question aussi faible que vous le pensiez un instant? Parce que, lorsque je lis des livres C # /. NET, msdn ou que je parle à des développeurs C #, je suis confus pour les raisons suivantes:
ref
dans les arguments d'une fonction, e.g. ...(ref Type someObject)
ne sent pas bon pour eux et ils suggèrent ...(Type someObject)
, je ne comprends vraiment pas clairement cette suggestion. Les raisons que j'ai entendues: mieux vaut travailler avec la copie de l'objet, puis l'utiliser comme valeur de retour, ne pas corrompre la mémoire par une référence etc ... J'entends souvent une telle explication sur les objets de connexion DB. Comme pour mon expérience en C/C++, je ne comprends vraiment pas pourquoi utiliser une référence est une mauvaise chose en C #? Je contrôle la durée de vie de l'objet et ses allocations/ré-allocations de mémoire etc ... Je ne lis dans les livres et forums que les conseils it's bad, because you can corrupt your connection and cause a memory leak by a reference lose
, donc je contrôle la vie de l'objet, je peux contrôler manuellement ce que je veux vraiment, alors pourquoi est-ce mauvais?ref
un pointeur (*
) ou une référence comme en C++ par &
? Comme je me souviens des pointeurs en C/C++ allouent toujours un espace avec une taille de void*
type - 4 octets (la taille valide dépend de l'architecture), où héberge une adresse vers une structure ou une variable. En C++ en passant une référence &
il n'y a pas de nouvelles allocations à partir du tas/pile et vous travaillez avec des objets déjà définis dans l'espace mémoire et il n'y a pas de mémoire sous-allouée pour un pointeur en externe comme en simple C. Alors, quel est le ref
en C # ? .NET VM le gère-t-il comme un pointeur en C/C++ simple et son GC
alloue de l'espace temporaire pour un pointeur ou fait-il un travail comme référence en C++? ref
fonctionne uniquement avec un type géré correctement ou pour des types de valeur comme bool, int
il vaut mieux changer un code unsafe
et passer par un pointeur dans un style non managé?En C #, lorsque vous voyez quelque chose faisant référence à un type de référence (c'est-à-dire un type déclaré avec class
au lieu de struct
), alors vous traitez toujours l'objet avec un pointeur. En C++, tout est un type de valeur par défaut, alors qu'en C # tout est un type de référence par défaut.
Lorsque vous dites "ref" dans la liste des paramètres C #, ce que vous dites vraiment ressemble plus à un "pointeur vers un pointeur". Vous dites que, dans la méthode, vous voulez remplacer non pas le contenu de l'objet, mais la référence à l'objet lui-même, dans le code appelant votre méthode.
À moins que ce ne soit votre intention, vous devez simplement passer directement le type de référence; en C #, transmettre des types de référence est bon marché (semblable à passer une référence en C++).
Apprenez/comprenez la différence entre les types de valeur et les types de référence en C #. Ils sont un concept majeur dans ce langage et les choses vont vraiment être déroutantes si vous essayez de penser en utilisant le modèle objet C++ dans le pays C #.
Les programmes suivants sont essentiellement sémantiquement équivalents:
#include <iostream>
class AClass
{
int anInteger;
public:
AClass(int integer)
: anInteger(integer)
{ }
int GetInteger() const
{
return anInteger;
}
void SetInteger(int toSet)
{
anInteger = toSet;
}
};
struct StaticFunctions
{
// C# doesn't have free functions, so I'll do similar in C++
// Note that in real code you'd use a free function for this.
static void FunctionTakingAReference(AClass *item)
{
item->SetInteger(4);
}
static void FunctionTakingAReferenceToAReference(AClass **item)
{
*item = new AClass(1729);
}
};
int main()
{
AClass* instanceOne = new AClass(6);
StaticFunctions::FunctionTakingAReference(instanceOne);
std::cout << instanceOne->GetInteger() << "\n";
AClass* instanceTwo;
StaticFunctions::FunctionTakingAReferenceToAReference(&instanceTwo);
// Note that operator& behaves similar to the C# keyword "ref" at the call site.
std::cout << instanceTwo->GetInteger() << "\n";
// (Of course in real C++ you're using std::shared_ptr and std::unique_ptr instead,
// right? :) )
delete instanceOne;
delete instanceTwo;
}
Et pour C #:
using System;
internal class AClass
{
public AClass(int integer)
: Integer(integer)
{ }
int Integer { get; set; }
}
internal static class StaticFunctions
{
public static void FunctionTakingAReference(AClass item)
{
item.Integer = 4;
}
public static void FunctionTakingAReferenceToAReference(ref AClass item)
{
item = new AClass(1729);
}
}
public static class Program
{
public static void main()
{
AClass instanceOne = new AClass(6);
StaticFunctions.FunctionTakingAReference(instanceOne);
Console.WriteLine(instanceOne.Integer);
AClass instanceTwo = new AClass(1234); // C# forces me to assign this before
// it can be passed. Use "out" instead of
// "ref" and that requirement goes away.
StaticFunctions.FunctionTakingAReferenceToAReference(ref instanceTwo);
Console.WriteLine(instanceTwo.Integer);
}
}
Un ref
en C # équivaut à une référence C++:
Du code C++:
void foo(int& x)
{
x = 42;
}
// ...
int answer = 0;
foo(answer);
Code C # équivalent:
void foo(ref int x)
{
x = 42;
}
// ...
int answer = 0;
foo(ref answer);
Chaque référence en C # est un pointeur vers des objets sur le tas en tant que pointeur en C++ et la référence de C # est identique à & en C++
La raison pour laquelle il faut éviter ref est que C # fonctionne sur le principe que cette méthode ne doit pas modifier l'objet passé en paramètre, car pour quelqu'un qui n'a pas de source de méthode, il se peut qu'il ne sache pas si cela entraînera une perte de données ou non.
String a = " A ";
String b = a.Trim();
Dans ce cas, je suis convaincu qu'un reste intact. En mathématiques, le changement doit être considéré comme une affectation qui indique visuellement que b est modifié ici par le consentement du programmeur.
a = a.Trim();
Ce code modifiera un lui-même et le codeur en est conscient.
Pour conserver cette méthode de changement par affectation, la référence doit être évitée, sauf cas exceptionnel.
Cela ressemble à un cauchemar de disposer/complet. Si j'ai un objet dont les événements sont enregistrés et que je le passe dans une fonction par référence et que cette référence est ensuite réallouée, la disposition doit être appelée ou la mémoire sera allouée jusqu'à la fermeture du programme. Si le disposer est appelé, tout ce qui est enregistré dans les objets ne sera plus enregistré et tout ce pour quoi il sera enregistré ne sera plus enregistré. Comment est-ce que quelqu'un garderait ça droit? Je suppose que vous pouvez comparer les adresses mémoire et essayer de ramener les choses à la raison si vous ne devenez pas fou.
C # n'a pas d'équivalent de pointeurs C++ et fonctionne sur les références. ref
ajoute un niveau d'indirection. Il fait de l'argument de type valeur une référence et lorsqu'il est utilisé avec le type de référence, il en fait une référence à une référence.
En bref, il permet de reporter toute modification d'un type de valeur en dehors d'un appel de méthode. Pour le type de référence, il permet de remplacer la référence d'origine à un objet totalement différent (et pas seulement de changer le contenu de l'objet). Il peut être utilisé si vous souhaitez réinitialiser un objet à l'intérieur d'une méthode et que la seule façon de le faire est de le recréer. Bien que j'essaierais d'éviter une telle approche.
Donc, pour répondre à votre question, ref
serait comme une référence C++ à une référence.
MODIFIER
Ce qui précède est vrai pour le code sécurisé. Les pointeurs existent en C # dangereux et sont utilisés dans certains cas très spécifiques.