Przekazywanie obiektów przez odniesienie lub wartość w C#
W C# zawsze myślałem, że nie-prymitywne zmienne są przekazywane przez odniesienia i prymitywne wartości przekazywane przez wartość.
Tak więc, gdy przekazujemy do metody dowolny Nie-prymitywny obiekt, wszystko, co robimy z obiektem w metodzie, wpłynie na przekazywany obiekt. (C # 101 stuff)
Jednak zauważyłem, że kiedy przejdę System.Rysunek.Obiekt obrazu, że tak się nie wydaje? Jeśli zdam system.rysunek.obiektu obrazu do innej metody i załadować obraz na ten obiekt, a następnie niech ta metoda wyjdzie poza zakres i wrócić do metody wywołującej, że obraz nie jest załadowany na oryginalny obiekt?
Dlaczego tak jest?
7 answers
Obiekty w ogóle nie są przekazywane. Domyślnie argument jest obliczany, a jego wartość jest przekazywana jako wartość początkowa parametru metody, którą wywołujesz. Teraz ważne jest to, że wartość jest referencją dla typów referencyjnych - sposobem na dotarcie do obiektu (lub null). Zmiany w tym obiekcie będą widoczne z wywołującego. Jednak zmiana wartości parametru na odnoszącą się do innego obiektu spowoduje, że nie będzie widoczna podczas używania wartość pass by, która jest domyślna dla wszystkich typów .
Jeśli chcesz użyć pass-by-reference, musisz użyć out
lub ref
, niezależnie od tego, czy parametr type jest typem wartości, czy typem odniesienia. W takim przypadku, efektywnie sama zmienna jest przekazywana przez referencję, więc parametr używa tej samej lokalizacji przechowywania co argument - a zmiany w samym parametrze są postrzegane przez wywołującego.
Więc:
public void Foo(Image image)
{
// This change won't be seen by the caller: it's changing the value
// of the parameter.
image = Image.FromStream(...);
}
public void Foo(ref Image image)
{
// This change *will* be seen by the caller: it's changing the value
// of the parameter, but we're using pass by reference
image = Image.FromStream(...);
}
public void Foo(Image image)
{
// This change *will* be seen by the caller: it's changing the data
// within the object that the parameter value refers to.
image.RotateFlip(...);
}
Mam artykuł, który idzie o wiele więcej szczegóły w tym . Zasadniczo, "pass by reference" nie oznacza tego, co myślisz, że to znaczy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-03 06:24:57
Jeszcze jedna próbka kodu do zaprezentowania tego:
void Main()
{
int k = 0;
TestPlain(k);
Console.WriteLine("TestPlain:" + k);
TestRef(ref k);
Console.WriteLine("TestRef:" + k);
string t = "test";
TestObjPlain(t);
Console.WriteLine("TestObjPlain:" +t);
TestObjRef(ref t);
Console.WriteLine("TestObjRef:" + t);
}
public static void TestRef(ref int i)
{
i = 5;
}
public static void TestPlain(int i)
{
i = 5;
}
public static void TestObjRef(ref string s)
{
s = "TestObjRef";
}
public static void TestObjPlain(string s)
{
s = "TestObjPlain";
}
I wyjście:
TestPlain:0
TestRef:5
TestObjPlain: test
TestObjRef: TestObjRef
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-12 14:34:01
Myślę, że jest jaśniejszy, gdy robisz to w ten sposób. Polecam pobranie LinkPad , aby przetestować takie rzeczy.
void Main()
{
var Person = new Person(){FirstName = "Egli", LastName = "Becerra"};
//Will update egli
WontUpdate(Person);
Console.WriteLine("WontUpdate");
Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");
UpdateImplicitly(Person);
Console.WriteLine("UpdateImplicitly");
Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");
UpdateExplicitly(ref Person);
Console.WriteLine("UpdateExplicitly");
Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");
}
//Class to test
public class Person{
public string FirstName {get; set;}
public string LastName {get; set;}
public string printName(){
return $"First name: {FirstName} Last name:{LastName}";
}
}
public static void WontUpdate(Person p)
{
//New instance does jack...
var newP = new Person(){FirstName = p.FirstName, LastName = p.LastName};
newP.FirstName = "Favio";
newP.LastName = "Becerra";
}
public static void UpdateImplicitly(Person p)
{
//Passing by reference implicitly
p.FirstName = "Favio";
p.LastName = "Becerra";
}
public static void UpdateExplicitly(ref Person p)
{
//Again passing by reference explicitly (reduntant)
p.FirstName = "Favio";
p.LastName = "Becerra";
}
I to powinno wyjść
WontUpdate
Imię: Egli, nazwisko: Becerra
Updateimplicite
Imię: Favio, nazwisko: Becerra
Updateexplicite
Imię: Favio, nazwisko: Becerra
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-16 04:05:45
Kiedy przekazujesz obiekt typu System.Drawing.Image
do metody, przekazujesz kopię odniesienia do tego obiektu.
Więc jeśli wewnątrz tej metody ładujesz nowy obraz, ładujesz go za pomocą nowego/skopiowanego odniesienia. Nie zmienisz oryginału.
YourMethod(System.Drawing.Image image)
{
//now this image is a new reference
//if you load a new image
image = new Image()..
//you are not changing the original reference you are just changing the copy of original reference
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-12 14:14:20
Jak przekazałeś obiekt method?
Czy robisz nową wewnątrz tej metody dla obiektu? Jeśli tak, musisz użyć ref
w metodzie.
Następujący link daje lepszy pomysł.
Http://dotnetstep.blogspot.com/2008/09/passing-reference-type-byval-or-byref.html
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-12 14:13:44
W Pass By Reference dodajesz tylko "ref" w parametrach funkcji i jeden
kolejną rzeczą, którą powinieneś zadeklarować jako "static", ponieważ main to static (#public void main(String[] args)
)!
namespace preparation
{
public class Program
{
public static void swap(ref int lhs,ref int rhs)
{
int temp = lhs;
lhs = rhs;
rhs = temp;
}
static void Main(string[] args)
{
int a = 10;
int b = 80;
Console.WriteLine("a is before sort " + a);
Console.WriteLine("b is before sort " + b);
swap(ref a, ref b);
Console.WriteLine("");
Console.WriteLine("a is after sort " + a);
Console.WriteLine("b is after sort " + b);
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-27 02:07:04
Przyjęta odpowiedź brzmi trochę źle i myląco. Co to jest " kopia referencji?"
W jaki sposób poniższe stwierdzenie ma sens?:
" jednak zmiana wartości parametru na inny obiekt nie będzie widoczna, gdy używasz wartości pass by, która jest domyślna dla wszystkich typów."Wartość Pass by nie jest domyślna dla wszystkich typów.
Jego przykład w linku próbuje ustawić instancję obiektu na null. Obiekt nie został pomyślnie ustawiona na null z powodu automatycznego usuwania śmieci. Nie można go usunąć w ten sposób.
Oto artykuł Microsoft porównujący Javę i C#.
Z https://msdn.microsoft.com/en-us/library/ms836794.aspx
" Wszystkie obiekty są referencjami
Typy referencji są bardzo podobne do wskaźników w C++, szczególnie podczas ustawiania identyfikatora jakiejś nowej instancji klasy. Ale podczas uzyskiwania dostępu do Właściwości lub metod tego typu odniesienia, użyj"."operator, który jest podobny do dostępu do instancji danych w C++, które są tworzone na stosie. Wszystkie instancje klas są tworzone na stercie za pomocą nowego operatora, ale usuwanie nie jest dozwolone, ponieważ oba języki używają własnych schematów usuwania śmieci, omówionych poniżej."
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-26 10:35:55