Передача объектов по ссылке или значению в C#
В C# я всегда думал, что непримитивные переменные передаются по ссылке, а примитивные значения передаются по значению.
поэтому при передаче в метод любого непримитивного объекта все, что сделано с объектом в методе, повлияет на передаваемый объект. (C# 101 материал)
однако я заметил, что когда я передаю систему.Рисунок.Изображение объекта, что это, кажется, не так? Если я пройду систему.рисунок.объект изображения к другому методу, и нагружает изображение на этот объект, а затем пусть этот метод выходит из области видимости и вернуться к вызывающему методу, что изображение не загружается на исходный объект?
Почему это?
7 ответов:
объекты не передаются вообще. По умолчанию вычисляется аргумент и его стоимостью передается по значению в качестве начального значения параметра вызываемого метода. Теперь важным моментом является то, что значение является ссылкой для ссылочных типов - способом получения объекта (или null). Изменения этого объекта будут видны из вызывающего объекта. Однако изменение значения параметра для ссылки на другой объект будет не будет видимый, когда вы используете pass by value, который является значением по умолчанию для все типы.
Если вы хотите использовать pass-by-reference, вы должны использовать
outилиref, является ли тип параметра типом значения или ссылочным типом. В этом случае фактически сама переменная передается по ссылке, поэтому параметр использует то же место хранения, что и аргумент - и изменения самого параметра видны абонент.Так:
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(...); }у меня есть статья, которая идет в гораздо более подробно в этом. В принципе, "пройти по ссылке" не означает, что вы думаете, что это значит.
еще один пример кода, чтобы продемонстрировать это:
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"; }и выход:
TestPlain: 0
TestRef: 5
TestObjPlain: test
TestObjRef: TestObjRef
Я думаю яснее, когда вы делаете это, как это. Я рекомендую скачать LinkPad чтобы проверить такие вещи.
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"; }и это должно выводить
WontUpdate
имя: Эгли, фамилия: Бесерра
UpdateImplicitly
имя: Фавио, фамилия: Бесерра
UpdateExplicitly
имя: Фавио, фамилия: Бесерра
когда вы передаете the
System.Drawing.Imageвведите объект в метод, который вы фактически передаете копию ссылки на этот объект.поэтому, если внутри этого метода вы загружаете новое изображение, которое вы загружаете с помощью новой/скопированной ссылки. Вы не вносите изменения в оригинал.
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 }
Как вы передали объект методу?
вы делаете новое внутри этого метода для объекта? Если это так, вы должны использовать
refв способ.следующая ссылка даст вам лучшее представление.
http://dotnetstep.blogspot.com/2008/09/passing-reference-type-byval-or-byref.html
в Pass by Reference вы добавляете только "ref" в параметрах функции и один больше того, вы должны объявлять функцию "static" из-за main is 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); } } }
принятый ответ звучит немного неправильно и запутанно. Что такое " копия ссылки?"
Как следующее утверждение имеет смысл?:
" однако изменение значения параметра для ссылки на другой объект не будет видно, когда вы используете pass by value, который является значением по умолчанию для всех типов."Pass by value не является значением по умолчанию для всех типов.
его пример в своей ссылке пытается установить экземпляр объекта в null. Объект не удалось установить значение null из-за автоматической сборки мусора. Он не может быть удален таким способом.
вот статья Microsoft, сравнивающая Java и C#.
от https://msdn.microsoft.com/en-us/library/ms836794.aspx
"все объекты являются ссылками
ссылочные типы очень похожи на указатели в C++, особенно при установке идентификатора на какой-то новый экземпляр класса. Но при доступе к свойствам или методам этот ссылочный тип, используйте "."оператор, который аналогичен доступу к экземплярам данных в C++, которые создаются в стеке. Все экземпляры класса создаются в куче с помощью оператора new, но удаление не допускается, так как оба языка используют свои собственные схемы сборки мусора, описанные ниже."
Comments