13

Tôi đang sử dụng C# với khung công tác Unity của Microsoft. Tôi không hoàn toàn chắc chắn làm thế nào để giải quyết vấn đề này. Nó có thể có một cái gì đó để làm với tôi thiếu hiểu biết DI với Unity.Constructor Injection trong C#/Unity?

Vấn đề của tôi có thể tóm tắt bằng cách sử dụng mã ví dụ sau đây:

class Train(Person p) { ... } 

class Bus(Person p) { ... } 

class Person(string name) { ... } 

Person dad = new Person("joe"); 
Person son = new Person("timmy"); 

Khi tôi gọi phương thức quyết tâm trên xe buýt làm thế nào tôi có thể chắc chắn rằng Person 'con trai' với tên 'timmy' được tiêm và khi giải quyết bằng cách nào tôi có thể chắc chắn rằng 'Người cha' với tên 'joe' được giải quyết?

Tôi đang nghĩ có thể sử dụng các trường hợp được đặt tên? Nhưng tôi đang thua lỗ. Bất kỳ trợ giúp sẽ được đánh giá cao.

Ngoài ra, tôi không muốn tạo giao diện IPerson.

Trả lời

15

Một cách để giải quyết vấn đề này là sử dụng một hàm tạo tiêm với đăng ký có tên.

// Register timmy this way 
Person son = new Person("Timmy"); 
container.RegisterInstance<Person>("son", son); 

// OR register timmy this way 
container.RegisterType<Person>("son", new InjectionConstructor("Timmy")); 

// Either way, register bus this way. 
container.RegisterType<Bus>(new InjectionConstructor(container.Resolve<Person>("son"))); 

// Repeat for Joe/Train 
+2

làm cách nào để lưu trữ các tệp này trong tệp cấu hình thay vì mã hóa cứng? –

32

Trừ khi bạn đăng ký tương ứng "joe" và "timmy" dưới dạng phụ thuộc có tên, bạn không thể chắc chắn rằng "timmy" được đưa vào Schoolbus. Thực tế, nếu bạn cố gắng đăng ký hai phiên bản của cùng một lớp với các phụ thuộc không tên, bạn sẽ có một thiết lập không rõ ràng và bạn sẽ không thể giải quyết được Person.

Nói chung, nếu bạn phải đăng ký nhiều trường hợp được đặt tên, có thể bạn đang nói về DI theo cách sai. Ý tưởng chính của DI là giải quyết Dịch vụ tên miền nhiều hơn Đối tượng miền.

Ý tưởng chính của DI là cung cấp một cơ chế cho phép bạn để giải quyết loại trừu tượng (giao diện hoặc lớp trừu tượng) thành các loại bê tông. Ví dụ của bạn không có loại trừu tượng, do đó, nó không thực sự làm cho rất nhiều ý nghĩa.

+0

Cảm ơn phản hồi sâu sắc của bạn. Mặc dù bạn có thể đúng, DI và IoC là những khái niệm mới với tôi vì vậy tôi vẫn đang cố gắng tìm ra thiết kế tốt nhất. –

+1

Đó là công bằng đủ - bạn có thể muốn đọc sách của tôi, sau đó :) –

+2

JP, hãy kiểm tra cuốn sách của mình. Tôi đã tìm thấy nó rất sâu sắc. –

14

Mark Seeman đã làm đúng. Và tôi thông cảm với sự nhầm lẫn của bạn. Tôi đã tự mình trải qua khi tôi học cách sử dụng các thùng chứa phụ thuộc tự động. Vấn đề là có nhiều cách hợp lý và hợp lý để thiết kế và sử dụng các đối tượng. Tuy nhiên, chỉ một số phương pháp tiếp cận làm việc với các thùng chứa phụ thuộc tự động.

Lịch sử cá nhân của tôi: Tôi đã học các nguyên tắc OO về xây dựng đối tượng và Inversion Of Control từ lâu trước khi tôi học cách sử dụng các hộp chứa Inversion of Control như Unity hoặc Castle Windsor. Tôi có thói quen viết mã như sau:

public class Foo 
{ 
    IService _service; 
    int _accountNumber; 

    public Foo(IService service, int accountNumber) 
    { 
     _service = service; 
     _accountNumber = accountNumber; 
    } 
    public void SaveAccount() 
    { 
     _service.Save(_accountNumber); 

    } 
} 
public class Program 
{ 
    public static void Main() 
    { 
     Foo foo = new Foo(new Service(),1234); 
     foo.Save(); 
    } 
} 

Trong thiết kế này, lớp Foo của tôi chịu trách nhiệm lưu tài khoản vào cơ sở dữ liệu. Nó cần một số tài khoản để làm điều đó và một dịch vụ để làm công việc bẩn thỉu. Điều này hơi giống với các lớp cụ thể mà bạn đã cung cấp ở trên, trong đó mỗi đối tượng có một số giá trị duy nhất trong hàm tạo. Điều này làm việc tốt khi bạn khởi tạo các đối tượng bằng mã của riêng bạn. Bạn có thể chuyển các giá trị thích hợp vào đúng thời điểm.

Tuy nhiên, khi tôi tìm hiểu về các thùng chứa phụ thuộc tự động, tôi thấy rằng tôi không còn instantiating Foo bằng tay nữa. Các container sẽ nhanh chóng các đối số constructor cho tôi. Đây là một tiện ích tuyệt vời cho các dịch vụ như IService. Nhưng nó rõ ràng không hoạt động tốt cho các số nguyên và chuỗi và tương tự. Trong những trường hợp đó, nó sẽ cung cấp một giá trị mặc định (như số không cho một số nguyên).Thay vào đó, tôi đã quen với việc đi qua trong bối cảnh giá trị cụ thể như số tài khoản, tên, vv ... Vì vậy, tôi đã phải điều chỉnh phong cách của tôi về mã hóa và thiết kế là như thế này:

public class Foo 
{ 
    IService _service; 
    public Foo(IService service) 
    { 
     _service = service; 
    } 
    public void SaveAccount(int accountNumber) 
    { 
     _service.Save(accountNumber); 

    } 
} 
public class Program 
{ 
    public static void Main() 
    { 
     Foo foo = new Foo(new Service()); 
     foo.Save(1234); 
    } 
} 

Dường như cả hai Các lớp Foo là thiết kế hợp lệ. Nhưng thứ hai là có thể sử dụng với việc tiêm phụ thuộc tự động, và lần đầu tiên là không.

+4

Không nên phương thức Main() thứ 2 giống như: Foo foo = new Foo (new Service()); foo.Save (1234); ? – McBainUK

+0

Như một vấn đề của thực tế nó phải là foo.SaveAccount (1234) :) – sethidev

+0

Thú vị không phải về khởi tạo 'int' ở đó. Tôi chỉ có một trường hợp rất giống nhau (tên miền hoàn toàn khác), nơi tôi cần khởi tạo thời gian chờ - trừu tượng hóa một phương pháp, – Thomas