2010-06-25 13 views
6

Tôi đang tìm cách cải thiện tính nhất quán, ngắn gọn và khả năng đọc của một số mã trong ứng dụng tôi đang thực hiện. Mã bắt đầu trông giống như sau:Phương pháp mở rộng C# trên loại với đối số loại chung

context.GetGraphType<Bar>().Subscribe<Fizz>(
    (instance, evt) => e.Execute((Bar)instance.Instance) 
); 

Có một số dòng mã gần như giống hệt như trên. Tôi muốn viết lại nó để trông giống như sau:

typeof(Bar).SubscribeTo<Fizz>(context); 

Một điều, điều này sẽ cho phép tôi tận dụng chính thức những gì đã trở thành một quy ước không chính thức. Ngoài ra, tôi hy vọng rằng nó bây giờ sẽ đọc một cái gì đó như "thanh đăng ký cho sự kiện fizz trên bối cảnh nhất định", chứ không phải là "bối cảnh được loại thanh và đăng ký để fizz và sau đó hiện một số công cụ." Tôi nghĩ rằng dòng chảy là tốt hơn, và đồng nghiệp tôi hỏi về nó đã đồng ý.

Tôi bắt đầu triển khai phương thức này dưới dạng phương thức tiện ích mở rộng. Để thực hiện điều này, tôi muốn sử dụng lớp cơ sở chung trừu tượng cho loại sự kiện, vì vậy Fizz sẽ là Event<T>. Điều này có nghĩa là đối số kiểu chung cho phương thức mở rộng sẽ phải bị ràng buộc là kiểu mà phương thức mở rộng được gọi. Vì vậy, đối với ví dụ trên, Fizz sẽ phải thuộc loại Event<Bar>.

Điều này có khả thi không? Tôi đã đi với một giải pháp thay thế trong thời gian có nghĩa là, nhưng tôi vẫn còn tò mò nếu nó có thể được thực hiện. Các đề xuất khác cũng được hoan nghênh.

Cảm ơn!

Chỉnh sửa # 1: Chỉ cần rõ ràng, tôi nhận ra rằng tôi có thể sử dụng thông số loại bổ sung, nhưng tôi đang tìm cách tránh điều đó nếu có thể.

Chỉnh sửa # 2: Tôi nghĩ rằng tôi sẽ đi với một biến thể nhỏ của câu trả lời được chấp nhận, vì nó không phù hợp với 100% với kịch bản của tôi. Điểm mấu chốt là một lớp tĩnh chung có thể được sử dụng thay vì một phương thức mở rộng của Type để hoàn thành mục tiêu của tôi. Cảm ơn dss539!

Cập nhật mã (có thể có lỗi chính tả kể từ khi tôi đang làm điều này một cách nhanh chóng):

public class Bar { } 

public class Event<TSubscriber> 
{ 
    public abstract void Execute(TSubscriber source); 
} 

public class Fizz : Event<Bar> 
{ 
    public override void Execute(Bar bar) 
    { 
     // respond to event 
    } 
} 

public class Context { } 

public static class ForType<TSubscriber> 
{ 
    public static void SubscribeTo<TEvent>(Context context) 
     where TEvent : Event<TSubscriber> 
    { 
     context.GetType<TSubscriber>().Subscribe<TEvent>(
      (evt, args) => evt.Execute((TSubscriber)args.Source)); 
    } 
} 

public static void Run() 
{ 
    ForType<Bar>.SubscribeTo<Fizz>(context); 
} 
+1

Tôi không hiểu câu hỏi của bạn. Chữ ký phương thức hiện tại của bạn trông như thế nào? 'Đăng ký (loại Loại này, Hành động )'? Nếu bạn cho thấy những gì bạn có (hoặc tương đương), điều đó có thể giúp giải thích. – dss539

+0

Tôi nghĩ rằng tôi đã có một vấn đề thiết kế tương tự trong khi trở lại. Chúc may mắn :) – leppie

+0

@ dss539 Nó sẽ giống như Đăng ký (Loại loại này, Ngữ cảnh ctx). Vấn đề là không có cách nào (mà tôi biết) để hạn chế T là loại tham số Event . –

Trả lời

6

Đây không phải là chính xác như bạn hỏi, nhưng có lẽ nó sẽ đủ.

internal class Program 
{ 
    static void Main(string[] args) 
    { 
     var fizzHandler = new Fizz(); 
     var context = new Context(); 
     Handle<Bar>.With(fizzHandler, context); 
    } 
} 
public class Bar { } 
public class Event<T> { } 
public class Fizz : Event<Bar> { } 
public class Context { }; 
public static class Handle<T> 
{ 
    public static void With(Event<T> e, Context c) 
    { 
     //do your stuff 
    } 
} 
+0

Đây chính xác là những gì tôi đang tìm kiếm. Nó cho phép tôi hoàn thành mục tiêu ban đầu của tôi về khả năng đọc, mà không bỏ qua trình biên dịch. Cảm ơn! –

+0

@Bryan - vui vì tôi có thể giúp :) – dss539

4

Tại sao không làm điều gì đó một chút idomatic hơn, nơi bạn có thể sử dụng những hạn chế chung để thực thi các quy tắc:

public static class SubscriptionManager 
{ 
    public static void SubsribeTo<TSub,TEvent>(Context context) 
     where TEvent : Event<TSub> 
    { 
     /// you code... 
    } 
} 

Các cuộc gọi sẽ trông như thế:

SubscriptionManager.SubsribeTo<Bar,Fizz>(context); 

Ràng buộc where TEvent : Event<TSub> đảm bảo mối quan hệ giữa sự kiện và loại đăng ký mà bạn mong muốn. Nó cũng thích hợp hơn trong cuốn sách của tôi với một phương pháp mở rộng trên lớp Type - bởi vì điều đó có xu hướng lộn xộn intellisense. Type được sử dụng trong nhiều trường hợp và có các phương thức giả mạo xuất hiện trong Intellisense trên tất cả các trường hợp Type có xu hướng gây nhầm lẫn. Nó cũng không rõ ràng cho người tiêu dùng của thư viện rằng đây là cách để "đăng ký" - trừ khi họ đã thực sự nhìn thấy một ví dụ mã của nó.

+0

Cảm ơn câu trả lời của bạn! Tôi nhận thấy rằng ràng buộc có thể được thực thi theo cách này (xem ở trên chỉnh sửa). Đề xuất của bạn gần với đề xuất mà tôi đã xem xét, nhưng ưa thích phương pháp mở rộng. Tôi hiểu sự ác cảm khi sử dụng các phương thức mở rộng trên một lớp như Type, nhưng phương thức này được bản địa hóa thành một lớp duy nhất xử lý các đăng ký. Ngoài ra, nó không phải là một phương pháp thư viện mà sẽ được tiêu thụ bởi những người khác. –

0

Bạn có thể mở rộng gần System.Type (để có typeof(T).) và thêm phương thức (tiện ích mở rộng) vào ngữ cảnh biến đổi kiểu .NET thành biểu diễn kiểu nội bộ của bạn (giống như trả về GetGraphType).

static class Ext { 

    public static TypeofTypeofBar GetGraphTypeFromDotNetType(this Context ctx, Type t) { 
     return __something(t); 
    } 

    public static void SubscribeTo<F, E>(this Type type, Context ctx, E e) 
     where E: Event<T> { 
     context.GetGraphTypeFromDotNetType(type).Subscribe<F>(a); 
    } 

} 

...

typeof(Bar).SubscribeTo(context, action); 
+0

Hey Mau, chỉ như vậy xảy ra phương pháp bạn mô tả đã tồn tại. Tôi là một chút nhầm lẫn về các thông số loại trong ví dụ của bạn. Ý của bạn là "nơi E: Sự kiện "? Nếu đó là trường hợp tôi sẽ không cần phải vượt qua một "sự kiện" đối số, loại sẽ đủ (trong thực tế, tôi sẽ không thể cung cấp một trường hợp sự kiện). Tuy nhiên, có vẻ như loại thuê bao vẫn phải được cung cấp dưới dạng tham số kiểu. Vì vậy, về cơ bản bạn sẽ kết thúc với "typeof (Bar) .SubscribeTo (ctx)", đó là một biến thể khác mà tôi không hồi hộp. Nhờ đề nghị mặc dù! –