2009-03-23 43 views
24

Tôi có một lớp học với phương pháp nhà máy tĩnh trên đó. Tôi muốn gọi điện cho nhà máy để lấy một thể hiện của lớp, và sau đó làm thêm khởi, preferablly qua C# đối tượng initializer cú pháp:Có thể sử dụng bộ khởi tạo đối tượng C# với phương thức nhà máy không?

MyClass instance = MyClass.FactoryCreate() 
{ 
    someProperty = someValue; 
} 

vs

MyClass instance = MyClass.FactoryCreate(); 
instance.someProperty = someValue; 
+0

Chúc C# thêm một số đường cho phương pháp "Tạo" tĩnh như thế này (ví dụ như "Thêm" cho bộ sưu tập) :) – nawfal

Trả lời

25

Không. Hoặc bạn có thể chấp nhận một lambda như một đối số, mà cũng cung cấp cho bạn toàn quyền kiểm soát trong đó một phần của quá trình "sáng tạo" sẽ được gọi. Bằng cách này bạn có thể gọi nó thích:

MyClass instance = MyClass.FactoryCreate(c=> 
    { 
     c.SomeProperty = something; 
     c.AnotherProperty = somethingElse; 
    }); 

create sẽ trông tương tự như:

public static MyClass FactoryCreate(Action<MyClass> initalizer) 
{ 
    MyClass myClass = new MyClass(); 
    //do stuff 
    initializer(myClass); 
    //do more stuff 
    return myClass; 
} 

Một lựa chọn khác là để trở về một người thợ xây để thay thế (với một nhà điều hành diễn viên tiềm ẩn để MyClass). Mà bạn sẽ gọi như:

MyClass instance = MyClass.FactoryCreate() 
    .WithSomeProperty(something) 
    .WithAnotherProperty(somethingElse); 

Kiểm tra this cho trình tạo

Cả hai phiên bản đều được kiểm tra tại thời gian biên dịch và có hỗ trợ IntelliSense đầy đủ.


Một lựa chọn thứ ba mà đòi hỏi phải có một constructor mặc định:

//used like: 
var data = MyClass.FactoryCreate(() => new Data 
{ 
    Desc = "something", 
    Id = 1 
}); 
//Implemented as: 
public static MyClass FactoryCreate(Expression<Func<MyClass>> initializer) 
{ 
    var myclass = new MyClass(); 
    ApplyInitializer(myclass, (MemberInitExpression)initializer.Body); 
    return myclass ; 
} 
//using this: 
static void ApplyInitializer(object instance, MemberInitExpression initalizer) 
{ 
    foreach (var bind in initalizer.Bindings.Cast<MemberAssignment>()) 
    { 
     var prop = (PropertyInfo)bind.Member; 
     var value = ((ConstantExpression)bind.Expression).Value; 
     prop.SetValue(instance, value, null); 
    } 
} 

của nó giữa một giữa kiểm tra tại thời gian biên dịch và không được chọn. Nó cần một số công việc, vì nó đang buộc biểu hiện liên tục trên các bài tập. Tôi nghĩ rằng bất cứ điều gì khác là các biến thể của các phương pháp đã có trong các câu trả lời. Hãy nhớ rằng bạn cũng có thể sử dụng các bài tập bình thường, xem xét nếu bạn thực sự cần bất kỳ điều này.

+0

Tôi thích giải pháp lambda, gần như đúng cú pháp, nhưng cú pháp trình xây dựng yêu cầu tôi tạo một hàm cho mọi thuộc tính, điều này không khả thi, đặc biệt là trong tình huống nhà máy trừu tượng. –

+0

@Gaijin Tôi đồng ý, lambda là một cách thực sự nhanh chóng, tốt đẹp và được hỗ trợ tốt. Tôi sử dụng trình xây dựng cho mã thử nghiệm với các giá trị mặc định rõ ràng và một số phương thức không chỉ thiết lập thuộc tính. Đặc biệt nó thực sự hữu ích nếu MyClass là không thay đổi (vì bạn cần phải áp dụng tất cả nó ở hàm tạo). – eglasius

+0

@Gaijin đã đăng phiên bản thứ ba, cùng với một nhận xét nhắc rằng ok của nó cũng đi với các bài tập bình thường :) – eglasius

1

Không, đối tượng khởi tạo chỉ có thể là được sử dụng trên một cuộc gọi đến "mới" với hàm tạo. Một tùy chọn có thể là thêm một số arg bổ sung vào phương thức factory của bạn, để thiết lập các giá trị đó khi tạo đối tượng bên trong nhà máy.

MyClass instance = MyClass.FactoryCreate(int someValue, string otherValue); 
0

Không, đó là điều bạn chỉ có thể thực hiện 'nội tuyến'. Tất cả chức năng của nhà máy có thể làm cho bạn là trả về một tham chiếu.

1

Giống như mọi người đã nói, không.

Một lambda làm đối số đã được đề xuất.
Cách tiếp cận trang nhã hơn sẽ là chấp nhận ẩn danh và đặt thuộc tính theo đối tượng. tức là

MyClass instance = MyClass.FactoryCreate(new { 
    SomeProperty = someValue, 
    OtherProperty = otherValue 
}); 

Điều đó sẽ chậm hơn nhiều vì đối tượng sẽ phải được phản ánh cho tất cả các thuộc tính.

2

+1 trên "Không".

Dưới đây là một thay thế cho cách đối tượng ẩn danh:

var instance = MyClass.FactoryCreate(
    SomeProperty => "Some value", 
    OtherProperty => "Other value"); 

Trong trường hợp này FactoryCreate() sẽ là một cái gì đó như:

public static MyClass FactoryCreate(params Func<object, object>[] initializers) 
{ 
    var result = new MyClass(); 
    foreach (var init in initializers) 
    { 
     var name = init.Method.GetParameters()[0].Name; 
     var value = init(null); 
     typeof(MyClass) 
      .GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase) 
      .SetValue(result, value, null); 
    } 
    return result; 
} 
+0

Đẹp! Nó sẽ không nhanh hơn nếu bạn làm tương tự với một đối tượng Expression >, loại bỏ sự cần thiết phải tái cấu trúc? – configurator

+2

Để tự trả lời: Không, không. Nó sẽ khiến chúng ta phải biên dịch() biểu thức mọi lúc. Benchmark cho biết nó chậm hơn 30 lần ... – configurator

3

Bạn có thể sử dụng một phương pháp khuyến nông như sau:

namespace Utility.Extensions 
{ 
    public static class Generic 
    { 
     /// <summary> 
     /// Initialize instance. 
     /// </summary> 
     public static T Initialize<T>(this T instance, Action<T> initializer) 
     { 
      initializer(instance); 
      return instance; 
     } 
    } 
} 

Bạn sẽ gọi nó như sau:

using Utility.Extensions; 
// ... 
var result = MyClass.FactoryCreate() 
       .Initialize(x => 
       { 
        x.someProperty = someValue; 
        x.someProperty2 = someValue2; 
       }); 
Các vấn đề liên quan