2012-01-03 37 views
142

Trong C#, tôi đã luôn luôn nghĩ rằng các biến không nguyên thủy được chuyển bởi giá trị tham chiếu và nguyên thủy được truyền theo giá trị.Truyền đối tượng theo tham chiếu hoặc giá trị trong C#

Vì vậy, khi chuyển đến một phương thức bất kỳ đối tượng không nguyên thủy, bất cứ điều gì được thực hiện cho đối tượng trong phương thức sẽ ảnh hưởng đến đối tượng được truyền. (C# 101 stuff)

Tuy nhiên, tôi đã nhận thấy rằng khi tôi vượt qua một đối tượng System.Drawing.Image, điều này dường như không phải là trường hợp? Nếu tôi truyền một đối tượng system.drawing.image sang một phương thức khác, và tải một hình ảnh lên đối tượng đó, sau đó để cho phương thức đó nằm ngoài phạm vi và quay lại phương thức gọi, hình ảnh đó không được nạp trên đối tượng gốc?

Tại sao điều này?

+12

Tất cả các biến được chuyển theo giá trị theo mặc định trong C#. truyền * giá trị của tham chiếu * trong trường hợp các kiểu tham chiếu. –

+0

Điều tương tự: [tại sao-được-danh-khi-được-qua-không-chuyển-thành-một-chức năng-hành-như-được-qua-với-ref] (http://stackoverflow.com/questions/7321602/why-is-list-when-passed-without-ref-to-a-function-acting-like-passed-with-ref) – nawfal

Trả lời

315

Objects không được thông qua ở tất cả. Theo mặc định, đối số được đánh giá và giá trị của nó được chuyển, theo giá trị, làm giá trị ban đầu của thông số của phương thức bạn đang gọi. Bây giờ điểm quan trọng là giá trị là một tham chiếu cho các kiểu tham chiếu - một cách để tới một đối tượng (hoặc null). Thay đổi đối tượng đó sẽ hiển thị từ người gọi. Tuy nhiên, việc thay đổi giá trị của thông số để tham chiếu đến một đối tượng khác sẽ hiển thị không khi bạn đang sử dụng tính theo giá trị, đây là giá trị mặc định cho tất cả các loại.

Nếu bạn muốn sử dụng tính năng tham chiếu vượt qua, bạn phải sử dụng out hoặc ref, cho dù loại thông số là loại giá trị hay loại tham chiếu. Trong trường hợp đó, hiệu quả biến tự nó được truyền qua tham chiếu, do đó tham số sử dụng cùng một vị trí lưu trữ làm đối số - và thay đổi đối với tham số được người gọi nhìn thấy.

Vì vậy:

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(...); 
} 

Tôi có một article which goes into a lot more detail in this. Về cơ bản, "vượt qua bằng tham chiếu" không có nghĩa là bạn nghĩ nó có ý nghĩa gì.

+1

Quyền của bạn, tôi không thấy Tôi tải hình ảnh = Image.FromFile (..) và đó là thay thế các hình ảnh biến và không thay đổi đối tượng! :) Tất nhiên – michael

+0

trong những từ đơn giản tôi có thể nói nếu chúng ta thay đổi các thuộc tính hoặc gọi một số chức năng của tham số đối tượng nó sẽ bị ảnh hưởng nhưng nếu chúng ta bắt đầu p biến tham số sau đó nó sẽ là một ref đến vị trí/đối tượng mới. paramX.Caption = "asdasdasd"; // sẽ hoạt động paramX = new Object(); // sẽ ngắt kết nối nó khỏi người gọi và paramX đó sẽ được chuyển đến vị trí mới – Adeem

+1

@Adeem: Không hoàn toàn - không có "đối tượng tham số", có đối tượng là giá trị của thông số đề cập đến. Tôi nghĩ rằng bạn đã có ý tưởng đúng, nhưng thuật ngữ quan trọng :) –

2

Bạn đã chuyển đối tượng vào phương thức như thế nào?

Bạn đang làm mới bên trong phương pháp đó cho đối tượng nếu bạn phải sử dụng ref trong phương thức.

Liên kết sau cung cấp cho bạn ý tưởng tốt hơn.

http://dotnetstep.blogspot.com/2008/09/passing-reference-type-byval-or-byref.html http://msdn.microsoft.com/en-us/library/0f66670z(vs.71).aspx#vclrfpassingmethodparameters_referencetypes

2

Khi bạn chuyển đối tượng kiểu System.Drawing.Image cho một phương thức bạn đang thực sự chuyển một bản sao tham chiếu đến đối tượng đó.

Vì vậy, nếu bên trong phương thức đó bạn đang tải hình ảnh mới bạn đang tải bằng cách sử dụng tham chiếu mới/đã sao chép.Vì vậy, bạn không thực hiện sự thay đổi trong orignal

mẫ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 
} 
11

hơn Một mã:

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"; 
} 

Và kết quả:

TestPlain: 0

TestRef: 5

TestObjPlain: kiểm tra

TestObjRefTestObjRef

0

Trong đèo By Reference Bạn chỉ thêm "ref" trong các thông số chức năng và thêm một điều bạn nên được tuyên bố chức năng "tĩnh" vì chính là tĩnh (# 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); 
     } 
    } 
} 
3

Tôi đoán rõ ràng hơn khi bạn làm như thế này. Tôi khuyên bạn nên tải xuống LinkPad để kiểm tra những thứ như thế này.

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"; 
} 

Và đó nên sản lượng

WontUpdate

First name: Egli, Last name: Becerra

UpdateImplicitly

First name: Favio, cuối Tên : Becerra

UpdateExplicitly

First name: Favio, Last name: Becerra

0

Câu trả lời được chấp nhận âm thanh một chút sai lầm và khó hiểu. "Bản sao tham chiếu" là gì?

Làm thế nào để tuyên bố sau có ý nghĩa ?:

"Tuy nhiên, thay đổi giá trị của tham số để chỉ một đối tượng khác nhau sẽ không được hiển thị khi bạn đang sử dụng đi qua giá trị, đó là mặc định cho các loại." Vượt qua giá trị không phải là giá trị mặc định cho tất cả các loại.

Ví dụ của anh ta trong liên kết của anh ấy cố gắng đặt một thể hiện của một đối tượng thành vô giá trị. Đối tượng không được đặt thành công thành rỗng do thu thập rác tự động. Nó không thể bị xóa theo cách này.

Đây là bài viết của Microsoft so sánh Java và C#.

Từ https://msdn.microsoft.com/en-us/library/ms836794.aspx

"Tất cả các đối tượng là tài liệu tham khảo

loại tham khảo rất giống với con trỏ trong C++, đặc biệt là khi thiết lập một định danh đối với một số cá thể của lớp mới.Nhưng khi truy cập các thuộc tính hoặc phương thức của kiểu tham chiếu này, hãy sử dụng "." toán tử, tương tự như truy cập các cá thể dữ liệu trong C++ được tạo trên ngăn xếp. Tất cả các cá thể lớp được tạo trên heap bằng cách sử dụng toán tử mới, nhưng không được phép xóa vì cả hai ngôn ngữ đều sử dụng lược đồ thu gom rác riêng của chúng, được thảo luận bên dưới. "

Các vấn đề liên quan