2009-10-21 3 views
51

Tôi có một lớp như dưới đây:một nhà xây dựng như một đại biểu - là nó có thể trong C#?

class Foo 
{ 
    public Foo(int x) { ... } 
} 

và tôi cần phải vượt qua một phương pháp nhất định một đại biểu như thế này:

delegate Foo FooGenerator(int x); 

Có thể vượt qua các nhà xây dựng trực tiếp như một giá trị FooGenerator, mà không cần phải nhập:

delegate(int x) { return new Foo(x); } 

?

EDIT: Để sử dụng cá nhân, câu hỏi đề cập đến .NET 2.0, nhưng gợi ý/phản hồi cho 3.0+ cũng được chào đón.

+0

Câu hỏi thú vị. Tôi tin rằng các nhà xây dựng là các phương pháp hiệu quả đến mức CLR có liên quan, nhưng tôi sẽ không biết cú pháp. – Noldorin

+3

Tôi quan tâm: tại sao bạn muốn làm điều đó? –

+0

Tôi nghi ngờ câu trả lời là không có tuy nhiên. – Noldorin

Trả lời

27

Nope, CLR không cho phép đại biểu ràng buộc để ConstructorInfo.

Bạn có thể tuy nhiên chỉ cần tạo riêng của bạn:

static T Make<T>(Action<T> init) where T : new() 
{ 
    var t = new T(); 
    init(t); 
    return t; 
} 

Cách sử dụng

var t = Make<Foo>(x => { x.Bar = "bar"; x.Baz = 1; }); 
+0

Bạn có thể thêm một số tham chiếu (liên kết MSDN?) Cho câu lệnh của bạn về ràng buộc với ConstructorInfo không? – akavel

+7

Một hàm tạo không tạo ra một đối tượng mới. Một Constructor hoạt động cùng với một thường trình phân bổ. – user7116

+0

@sixlettervariables: +1 - trông giống như một lời giải thích hợp lý. Điều đó nói rằng, tôi vẫn muốn nhìn thấy một số tài liệu tham khảo MSDN/C# -specification/... từ một ai đó. – akavel

2

Thật không may là, các nhà xây dựng không hoàn toàn giống như phương pháp và vì vậy bạn không thể tạo đại biểu chỉ cho họ. Tuy nhiên, đây là một ý tưởng thú vị, có lẽ với nhiều thông tin hơn, chúng tôi có thể đưa ra một số giải pháp thay thế tương tự như cú pháp.

+0

Bạn có thể xây dựng trên tuyên bố rằng "các nhà xây dựng không hoàn toàn (...) như các phương pháp" trong bối cảnh của các đại biểu? Tôi muốn đặc biệt là một số tài liệu tham khảo để MSDN/C# tài liệu tham khảo/tài liệu khác. – akavel

+0

Câu trả lời không phản hồi, thông tin duy nhất được cung cấp là 'Không' và 'nhà thầu không hoàn toàn giống với phương pháp' (tất nhiên là không). Có một câu trả lời khác giả vờ giải thích tại sao một cái gì đó là không thể bằng cách nói "nó không phải là". – jwg

5

Có vẻ như bạn có thể muốn sử dụng mẫu nhà máy của lớp học.

Factory Method Pattern

+0

Đó thực sự là những gì tôi đã sử dụng như một giải pháp thay thế, nhưng để viết câu hỏi tôi nghĩ cấu trúc 'đại biểu' dễ hiểu hơn. – akavel

+0

Mẫu Phương thức Nhà máy là tốt, nhưng chỉ khi bạn biết tất cả các loại có thể có lúc biên dịch. –

0

tôi đoán là nó không thể vì bạn sẽ vượt qua một phương pháp của một đối tượng chưa được tạo.

7

Tôi nghĩ càng ngắn gọn càng bạn sẽ nhận được (mà không cần di chuyển đến một mô hình nhà máy) sẽ là một cái gì đó với các phương pháp mang tính chất như thế này:

delegate Foo FooGenerator(int x); 

...  

void DoStuff() 
{ 
    YourDelegateConsumer(x => new Foo(x)); 
} 

này không làm đúng những gì bạn yêu cầu (vì bạn đang chuyển giao một delegate cho một phương thức nặc danh trả về một cá thể mới, chứ không phải là một ủy nhiệm trực tiếp cho hàm tạo), nhưng tôi không nghĩ rằng những gì bạn đang yêu cầu là hoàn toàn có thể.

Đây là, tất nhiên, giả sử bạn đang sử dụng 3.5+

+0

+1; Tôi thực sự biên dịch cho 2.0, và đó là lý do tại sao tôi phải làm việc với "đại biểu", nhưng khi câu hỏi cốt lõi là về cái gì khác, cấu trúc lambda chắc chắn nên được ghi nhớ. – akavel

49

Tôi giả sử bạn thường làm một cái gì đó như thế này như một phần của việc thực hiện nhà máy, nơi các loại thực aren không biết tại thời điểm biên dịch ...

Trước tiên, lưu ý rằng cách tiếp cận dễ dàng hơn có thể là bước init sau tạo, sau đó bạn có thể sử dụng Generics:

static T Create<T>({args}) where T : class, ISomeInitInterface, new() { 
    T t = new T(); 
    t.Init(args); 
    return t; 
} 

Sau đó, bạn có thể sử dụng MakeGenericMethod và/hoặc CreateDelegate.


Nếu không; bạn có thể thực hiện việc này khi đang bay với Expression (3.5) hoặc DynamicMethod (2.0).

Các Expression cách tiếp cận dễ dàng hơn để mã:

var param = Expression.Parameter(typeof(int), "val"); 
    var ctor = typeof(Foo).GetConstructor(new[] { typeof(int) }); 
    var lambda = Expression.Lambda<Func<int, Foo>>(
     Expression.New(ctor, param), param); 
    var func = lambda.Compile(); 
    Foo foo = func(123); 
    string s = foo.ToString(); // proof 

hoặc (sử dụng DynamicMethod):

ConstructorInfo ctor = typeof(Foo).GetConstructor(new[] { typeof(int) }); 
    DynamicMethod dm = new DynamicMethod("Create", typeof(Foo), 
      new Type[] { typeof(int) }, typeof(Foo), true); 
    ILGenerator il = dm.GetILGenerator(); 
    il.Emit(OpCodes.Ldarg_0); 
    il.Emit(OpCodes.Newobj, ctor); 
    il.Emit(OpCodes.Ret); 
    Converter<int, Foo> func = (Converter<int, Foo>) 
     dm.CreateDelegate(typeof(Converter<int, Foo>));   
    Foo foo = func(123); 
    string s = foo.ToString(); // proof 
+1

Uh oh; về mặt kỹ thuật, sử dụng sự phản chiếu & c. là chính xác (và tôi cũng nghĩ về nó một lúc), nhưng nó có những sai sót nghiêm trọng: 1) như đã thấy trong bình luận của bạn, nó nghiêm trọng làm tổn thương khả năng đọc (và làm cho mã ít súc tích thay vì nhiều hơn); 2) AFAIK, sự phản xạ chậm hơn so với các cấu trúc được hỗ trợ bởi ngôn ngữ, vì nó ngăn xếp thêm một mức trừu tượng. – akavel

+5

Sau khi được biên dịch cho một đại biểu, một cách tiếp cận dựa trên phản chiếu không chậm hơn, và có thể (nhân dịp) nhanh hơn, so với mã được biên dịch thông thường. Rõ ràng bạn chỉ biên dịch nó một lần và nhớ cache đại biểu. –

+3

+1. Điều này (việc thực hiện biểu thức) đã được hữu ích hơn cho tôi hơn là câu trả lời được chấp nhận, vì tôi cần cctor, không phải là ctor. –

2

Marc Gravell của câu trả lời cảm hứng cho tôi để giải pháp rất đơn giản sau đây:

static void Main() 
{ 
    Pet a = _MakeObject(typeof(Dog)); 
    Pet b = _MakeObject(typeof(Cat)); 
} 

private static Pet _MakeObject(Type type) 
{ 
    ConstructorInfo info = type.GetConstructor(new Type[0]); 
    return (Pet)info?.Invoke(null); 
} 

Hầu như cùng một điều nếu hàm tạo của bạn có tham số (trong th ví dụ: 1 param của loại int):

static void Main() 
{ 
    Pet a = _MakeObject(typeof(Dog), 5); 
    Pet b = _MakeObject(typeof(Cat), 7); 
} 

private static Pet _MakeObject(Type type, int age) 
{ 
    ConstructorInfo info = type.GetConstructor(new [] { typeof(int) }); 
    return (Pet)info?.Invoke(new object[] { age }); 
}