2012-10-29 28 views
7

Trong đoạn mã sau, tôi đang cố gắng có phương thức (Work) từ lớp TestClass thay đổi giá trị của một số biến trong chương trình chính mà không phải trả lại chúng. Các biến được truyền theo tham chiếu trong hàm tạo TestClass.C# - truyền tham số bằng tham chiếu đến hàm tạo sau đó sử dụng chúng từ phương thức

class Program 
{ 
    static void Main(string[] args) 
    { 
     int a, b, c, d; 
     a = 5; b = 10; c = 20; d = 25; 
     Console.WriteLine("Main before TestClass: a=" + a + " b=" + b + " c=" + c + " d=" + d); 
     TestClass testObj = new TestClass(ref a,ref b,ref c,ref d); 
     testObj.Work(); 
     Console.WriteLine("Main after TestClass: a=" + a + " b=" + b + " c=" + c + " d=" + d); 
     Console.ReadLine(); 
    } 
} 

public class TestClass 
{ 
    int _a, _b, _c, _d; 
    public TestClass(ref int a, ref int b, ref int c, ref int d) 
    { 
     _a = a; _b = b; _c = c; _d = d; 
    } 

    public void Work() 
    { 
     Console.WriteLine("Work before changing: a=" + _a + " b=" + _b + " c=" + _c + " d=" + _d); 
     _a = 0; _b = 1; _c = 2; _d = 3; 
     Console.WriteLine("Work after changing: a=" + _a + " b=" + _b + " c=" + _c + " d=" + _d); 
    } 
} 

Tuy nhiên mã này trả về:

Main before TestClass: a=5 b=10 c=20 d=25 
Work before changing: a=5 b=10 c=20 d=25 
Work after changing: a=0 b=1 c=2 d=3 
Main after TestClass: a=5 b=10 c=20 d=25 

Có cách nào để có phương pháp thay đổi các giá trị của các biến trong chương trình chính? Cảm ơn bạn!

+2

Bạn cần lưu trữ * tham chiếu * trong '_a', v.v. nhưng bạn vừa khai báo chúng là' int '. Khi nó quay ra, CLR không hỗ trợ khái niệm này - bạn * có thể * có các biến cục bộ là các tham chiếu, chứ không phải các trường. [Nhưng C# thậm chí không cho phép bạn khai báo các biến cục bộ như vậy] (http://blogs.msdn.com/b/ericlippert/archive/2011/06/23/ref-returns-and-ref-locals.aspx). –

Trả lời

3

Bạn sẽ khấm khá hơn tạo giấy gói của riêng bạn trên Int32 để các thay đổi được phản ánh, bởi vì một khi các giá trị được gán cho các lĩnh vực của lớp, họ không còn tài liệu tham khảo, nhưng trường hợp khá khác nhau của Int32 . Hãy xem xét mã sau:

class Integer { 
    public int Value; 

    public Integer(int value) { 
     Value = value; 
    } 

    public override string ToString() { 
     return Value.ToString(); 
    } 
} 

class TestClass { 
    Integer _a, _b, _c, _d; 

    public TestClass(Integer a, Integer b, Integer c, Integer d) { 
     _a = a; 
     _b = b; 
     _c = c; 
     _d = d; 
    } 

    public void Work() { 
     _a.Value = 111; 
     _b.Value = 222; 
     _c.Value = 333; 
     _d.Value = 444; 
    } 
} 

Vì vậy, bây giờ bạn có một số nguyên - một lớp bọc trên Int32. Việc sử dụng sẽ mang lại cho bạn kết quả:

Integer a = new Integer(0), b = new Integer(0), c = new Integer(0), d = new Integer(0); 
Console.WriteLine("a: {0}, b: {1}, c: {2}, d: {3}", a, b, c, d); 
new TestClass(a, b, c, d).Work(); 
Console.WriteLine("a: {0}, b: {1}, c: {2}, d: {3}", a, b, c, d); 

Đầu ra là:

a: 0, b: 0, c: 0, d: 0 
a: 111, b: 222, c: 333, d: 444 

Bạn cũng có thể tìm thấy nó hữu ích để đọc thêm về các lớp học và cấu trúc trong C#, ví dụ http://msdn.microsoft.com/en-us/library/ms173109.aspx. (Int32 là một cấu trúc, trong khi ở trường hợp của bạn có thể bạn cần một lớp)

+0

Cảm ơn bạn, nó đã hoạt động! – panostra

3

Không thể. Bạn không thể lưu trữ tham chiếu đến một thành viên int và chỉ int a là bản sao - các thay đổi đối với bản sao không thay đổi bản gốc, như bạn đã thấy.

Bạn có thể quấn int vào trong một lớp học. Bạn có thể lưu trữ một tham chiếu đến lớp và hoạt động trên nó sau đó.

public class IntWrapper 
{ 
    public IntWrapper(int val = 0) { Value = val; } 
    public int Value { get; set; } 
} 

static void Main(string[] args) 
{ 
    IntWrapper a = new IntWrapper(5); 
    TestClass testObj = new TestClass(a); 
    testObj.Work(); 
} 

public class TestClass 
{ 
    IntWrapper _a; 
    public TestClass(IntWrapper a) 
    { 
     _a = a; 
    } 

    public void Work() 
    { 
     Console.WriteLine("Work before changing: a=" + _a.Value); 
     _a.Value = 0; 
     Console.WriteLine("Work after changing: a=" + _a.Value); 
    } 
} 
2

Điều bạn đang cố gắng làm không thể thực hiện được vì nó không được thiết kế để sử dụng theo cách đó. Bạn sẽ phải có các tham số đi trực tiếp vào phương thức Work thay vì, ví dụ:

testObj.Work(ref a, ref b, ref c, ref d); 
2

Hành vi này là do:

int _a, _b, _c, _d; 
public TestClass(ref int a, ref int b, ref int c, ref int d) 
{ 
    _a = a; _b = b; _c = c; _d = d; 
} 

int là một cấu trúc (một kiểu giá trị). Điều này có nghĩa là bạn KHÔNG chuyển tham chiếu (hoặc con trỏ) đến các trường như bạn mong đợi, ngay cả khi bạn sử dụng từ khóa ref. Những gì bạn đang làm thay vào đó là gán cho các trường các giá trị số nguyên được đưa ra làm đầu vào, không có gì hơn.
Để đạt được những gì bạn đang mong đợi bạn phải sử dụng một loại tài liệu tham khảo (quấn nguyên của bạn thành một lớp) hoặc tham khảo các số nguyên của bạn (thông qua các từ khóa ref) trong phương pháp này, nơi bạn có thực sự thay đổi giá trị của họ:

public void Work(ref int a, ref int b, ref int c, ref int d) 
{ 
    Console.WriteLine("Work before changing: a=" + a + " b=" + b + " c=" + c + " d=" + _d); 
    a = 0; b = 1; c = 2; d = 3; 
    Console.WriteLine("Work after changing: a=" + a + " b=" + b + " c=" + c + " d=" + _d); 
} 

Hiệu quả của việc đi ngang qua tham chiếu là rằng bất kỳ thay đổi các tham số trong phương pháp gọi được phản ánh trong gọi phương pháp.

Để hiểu thêm về từ khóa ref, hãy xem: ref (C# Reference).

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