2009-04-27 19 views
8

vậy, initializers đối tượng là tất cả các loại tiện dụng - đặc biệt là nếu bạn đang làm LINQ, nơi họ đang hết sức cần thiết - nhưng tôi không thể khá tìm ra cái này:sử dụng lồng nhau của C# Object Initializers

public class Class1 { 
    public Class2 instance; 
} 

public class Class2 { 
    public Class1 parent; 
} 

sử dụng như thế này:

Class1 class1 = new Class1(); 
class1.instance = new Class2(); 
class1.parent = class1; 

như một initializer:

Class1 class1 = new Class1() { 
    instance = new Class2() { 
     parent = class1 
    } 
}; 

này không hoạt động, class1 được cho là một biến cục bộ chưa được gán. nó thậm chí còn phức tạp hơn trong Linq khi bạn đang làm một cái gì đó như

select new Class1() { ... 

thậm chí nó không có tên để tham chiếu!

làm cách nào để giải quyết vấn đề này? tôi có thể chỉ đơn giản là không thực hiện tham chiếu lồng nhau bằng cách sử dụng khởi tạo đối tượng?

Trả lời

6

tôi có thể chỉ đơn giản là không làm tài liệu tham khảo lồng nhau bằng initializers đối tượng?

Bạn nói đúng - bạn không thể. Sẽ có một chu kỳ; A yêu cầu B để khởi tạo nhưng B yêu cầu A trước đó. Để chính xác - tất nhiên bạn có thể tạo các bộ khởi tạo đối tượng lồng nhau nhưng không phải với các phụ thuộc vòng tròn.

Nhưng bạn có thể - và tôi khuyên bạn nên làm nếu có thể - làm việc này như sau.

public class A 
{ 
    public B Child 
    { 
     get { return this.child; } 
     set 
     { 
     if (this.child != value) 
     { 
      this.child = value; 
      this.child.Parent = this; 
     } 
     } 
    } 
    private B child = null; 
} 

public class B 
{ 
    public A Parent 
    { 
     get { return this.parent; } 
     set 
     { 
     if (this.parent != value) 
     { 
      this.parent = value; 
      this.parent.Child = this; 
     } 
     } 
    } 
    private A parent = null; 
} 

Xây dựng mối quan hệ bên trong tài sản có lợi ích là bạn không thể quên một trong các câu lệnh khởi tạo. Nó là khá rõ ràng rằng đây là một giải pháp tối ưu bởi vì bạn cần hai báo cáo để có được một điều thực hiện.

b.Parent = a; 
a.Child = b; 

Với logic trong thuộc tính, bạn hoàn thành công việc chỉ với một câu lệnh.

a.Child = b; 

Hoặc cách khác.

b.Parent = a; 

Và cuối cùng với cú pháp khởi tạo đối tượng.

A a = new A { Child = new B() }; 
+0

Tôi thích câu trả lời này tốt nhất nhưng nó không hoạt động đối với tôi, vì tôi rời ra rằng Class2 thực sự là Danh sách , để làm cho các ví dụ rõ ràng hơn. drat, nó bị phản tác dụng. –

1

Sự cố này không dành riêng cho trình khởi tạo đối tượng, đó là hạn chế chung trong ngôn ngữ C#. Bạn không thể sử dụng biến cục bộ cho đến khi nó được gán chắc chắn. Đây là một đơn giản repro

Class1 Foo(Class1 c1) { 
    return c1; 
} 

void Example() { 
    Class1 c1 = Foo(c1); 
} 
1

Tôi không nghĩ rằng có anyway là để làm được việc này, khi ứng dụng được instantiating đối tượng Class1 nó cần phải đã tạo ra các đối tượng Class2 đầu tiên để nó biết nơi tưởng nhớ các tài liệu tham khảo có vị trí. Bạn có thể thấy điều này nếu bạn thử như sau:

 Class1 myClass1 = null; 

     myClass1 = new Class1() 
     { 
      instance = new Class2 
      { 
       parent = myClass1 
      } 
     }; 

này sẽ biên dịch và chạy, nhưng tài sản cha mẹ của Class2 sẽ được null vì đó là những gì giá trị là lúc dòng bên trong mã được chạy, có nghĩa là dòng bên trong nhất là dòng đầu tiên được thực thi.

0

Bạn chắc chắn có thể sử dụng bộ khởi tạo đối tượng lồng nhau, tôi luôn làm việc đó.

Tuy nhiên, trong trường hợp cụ thể của bạn, đối tượng của bạn có tham chiếu vòng tròn. Bạn cần hoàn thành việc khởi tạo trước khi gán nó cho người khác. Nếu không, bạn đang giao trình biên dịch một số cổ điển chicken and egg problem mà nó không thể xử lý.

+0

nó không phải là một vấn đề gà và trứng, ít nhất không phải là cách tôi hiểu initializers: chỉ cú pháp đường để tuyên bố một đối tượng và thiết lập các thuộc tính của nó. –

+0

Vâng, lỗi "biến cục bộ chưa được gán" của bạn cho tôi biết khác ... –

+0

xin lỗi - "không nên" là tùy theo cách trình khởi tạo đối tượng được giải thích. Đối tượng đầu tiên được tạo, và sau đó các tham số của nó được gán giá trị. Vì tôi có thể làm điều này theo kiểu mà các đối tượng initiailizers được cho là biên dịch xuống, tôi có thể làm điều đó với các bộ khởi tạo đối tượng. –

2

Bạn không thể làm điều đó với Trình khởi tạo đối tượng. Tuy nhiên, bạn có thể làm các trick sử dụng mã propery:

class A 
{ 
    B b; 

    public B B 
    { 
     set 
     { 
      b = value; 
      b.a = this; 
     } 
     get 
     { 
      return b; 
     } 
    } 
} 

class B 
{ 
    public A a; 
} 

Gọi nó:

var a = new A { B = new B() }; 
0

dụ bạn không phản ánh thiết kế lớp tốt, IMO; nó không liên kết chặt chẽ và tạo ra một tham chiếu vòng tròn. Đó là những gì làm cho nó không thể khởi tạo chúng lại với nhau trong một biểu thức duy nhất.

Tôi khuyên bạn nên quay lại bảng vẽ và tái cấu trúc lớp học của bạn thành mối quan hệ cha/con. Tôi muốn sử dụng tiêm constructor trên lớp con và có con nói với cha mẹ rằng đó là con của nó.

Ví dụ:

public class ParentClass 
{ 
    public List<ChildClass> Children; 
    public void AddChild(ChildClass child) 
    { 
     Children.Add(child); 
     // or something else, etc. 
    } 
    // various stuff like making sure Children actually exists before AddChild is called 
} 

public class ChildClass 
{ 
    public ParentClass Parent; 
    public ChildClass(ParentClass parent) 
    { 
     Parent = parent; 
     Parent.AddChild(this); 
    } 
} 

Sau đó, trong mã của bạn gọi:

var parent = new ChildClass(new ParentClass()).Parent; 

Và, vâng, mà làm việc trong LINQ:

// qry, etc. 
select new ChildClass(new ParentClass()).Parent 

nhưng sau đó như thế nào tôi có làm tất cả các ChildClass không cùng một phiên bản ParentClass ? - Andy Hohorst

Sau đó, bạn phải biết lớp cha mẹ trước.

var parent = new ParentClass(); 
var child = new ChildClass(parent); 

hoặc

var parent = new ParentClass(); 
// qry, etc. 
select new ChildClass(parent) 
+0

nhưng sau đó làm thế nào để làm cho tất cả ChildClass có cùng một cá thể ParentClass? –

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