2008-10-10 35 views
69

Có thể định nghĩa một lớp trong C# như vậyC# Generics sẽ không cho phép Đại biểu Loại ràng buộc

class GenericCollection<T> : SomeBaseCollection<T> where T : Delegate 

tôi có thể không cho cuộc sống của tôi thực hiện đêm qua này trong .NET 3.5. Tôi đã cố gắng sử dụng

delegate, Delegate, Action<T> and Func<T, T>

Dường như với tôi rằng điều này nên được cho phép một cách nào đó. Tôi đang cố gắng triển khai EventQueue của riêng mình.

Tôi đã kết thúc chỉ làm điều này [nguyên thủy gần đúng tâm trí bạn].

internal delegate void DWork(); 

class EventQueue { 
    private Queue<DWork> eventq; 
} 

Nhưng sau đó tôi mất khả năng sử dụng lại cùng định nghĩa cho các loại chức năng khác nhau.

Suy nghĩ?

Trả lời

64

Một số lớp không có sẵn dưới dạng các đường viền chung - Giả sử là một lớp khác.

Đối với các đại biểu, gần nhất bạn có thể nhận được là ": lớp", có lẽ sử dụng phản chiếu để kiểm tra (ví dụ, trong constructor tĩnh) mà T một đại biểu:

static GenericCollection() 
{ 
    if (!typeof(T).IsSubclassOf(typeof(Delegate))) 
    { 
     throw new InvalidOperationException(typeof(T).Name + " is not a delegate type"); 
    } 
} 
+7

+1 cho: 1) bằng cách sử dụng hàm tạo tĩnh và 2) bao gồm thông báo chi tiết do điều kiện gỡ lỗi lạ xung quanh loại khởi tạo. –

+6

@MarcGravell: Không ném ngoại lệ trong trình khởi tạo tĩnh vi phạm 'CA1065: Không tăng ngoại lệ ở các vị trí không mong muốn' ... Tôi luôn luôn giả định rằng bạn nên sử dụng quy tắc phân tích mã tùy chỉnh để tìm cách sử dụng không hợp lệ của lớp thường không có sẵn trong thời gian chạy. –

13

Sửa : Một số đề xuất công việc ở quanh được đề xuất trong những bài viết này:

http://jacobcarpenters.blogspot.com/2006/06/c-30-and-delegate-conversion.html

http://jacobcarpenters.blogspot.com/2006_11_01_archive.html


Từ C# 2.0 specification chúng ta có thể đọc (20,7, ràng buộc):

Một lớp học kiểu chế phải đáp ứng các quy tắc sau đây:

  • Loại phải là một kiểu lớp.
  • Loại không được niêm phong.
  • Loại không được là một trong các loại sau: System.Array, System.Delegate, System.Enum hoặc System.ValueType.
  • Loại không được là đối tượng. Bởi vì tất cả các loại xuất phát từ đối tượng, một ràng buộc như vậy sẽ không có hiệu lực nếu nó được cho phép.
  • Tối đa một ràng buộc đối với một thông số loại đã cho có thể là loại lớp.

Và chắc chắn đủ VS2008 spits ra một lỗi:

error CS0702: Constraint cannot be special class 'System.Delegate' 

Đối với thông tin và điều tra về vấn đề này đọc here.

3

Đại biểu đã hỗ trợ chuỗi. Điều này không đáp ứng nhu cầu của bạn?

public class EventQueueTests 
{ 
    public void Test1() 
    { 
     Action myAction =() => Console.WriteLine("foo"); 
     myAction +=() => Console.WriteLine("bar"); 

     myAction(); 
     //foo 
     //bar 
    } 

    public void Test2() 
    { 
     Action<int> myAction = x => Console.WriteLine("foo {0}", x); 
     myAction += x => Console.WriteLine("bar {0}", x); 
     myAction(3); 
     //foo 3 
     //bar 3 
    } 

    public void Test3() 
    { 
     Func<int, int> myFunc = x => { Console.WriteLine("foo {0}", x); return x + 2; }; 
     myFunc += x => { Console.WriteLine("bar {0}", x); return x + 1; }; 
     int y = myFunc(3); 
     Console.WriteLine(y); 

     //foo 3 
     //bar 3 
     //4 
    } 

    public void Test4() 
    { 
     Func<int, int> myFunc = x => { Console.WriteLine("foo {0}", x); return x + 2; }; 
     Func<int, int> myNextFunc = x => { x = myFunc(x); Console.WriteLine("bar {0}", x); return x + 1; }; 
     int y = myNextFunc(3); 
     Console.WriteLine(y); 

     //foo 3 
     //bar 5 
     //6 
    } 

} 
+0

thats không thực sự là chức năng tôi đang tìm kiếm ... Tôi đã cố gắng tạo một ràng buộc kiểu trên lớp chung của tôi ... –

3

Tôi gặp phải một tình huống mà tôi cần phải giải quyết một số nội bộ là Delegate nhưng tôi muốn có một ràng buộc chung. Cụ thể, tôi muốn thêm một trình xử lý sự kiện bằng cách sử dụng sự phản chiếu, nhưng tôi muốn sử dụng một đối số chung cho đại biểu. Đoạn code dưới đây không làm việc, kể từ khi "Handler" là một loại biến, và trình biên dịch sẽ không đúc Handler-Delegate:

public void AddHandler<Handler>(Control c, string eventName, Handler d) { 
    c.GetType().GetEvent(eventName).AddEventHandler(c, (Delegate) d); 
} 

Tuy nhiên, bạn có thể vượt qua một chức năng mà không được chuyển đổi cho bạn. convert nhận một đối số Handler và trả về một Delegate:

public void AddHandler<Handler>(Control c, string eventName, 
        Func<Delegate, Handler> convert, Handler d) { 
     c.GetType().GetEvent(eventName).AddEventHandler(c, convert(d)); 
} 

Bây giờ trình biên dịch là hạnh phúc. Gọi phương thức dễ dàng. Ví dụ, gắn với sự kiện KeyPress trên Windows Forms điều khiển:

AddHandler<KeyEventHandler>(someControl, 
      "KeyPress", 
      (h) => (KeyEventHandler) h, 
      SomeControl_KeyPress); 

nơi SomeControl_KeyPress là mục tiêu của sự kiện. Điều quan trọng là lambda chuyển đổi - nó không hoạt động, nhưng nó thuyết phục trình biên dịch bạn đã cho nó một đại biểu hợp lệ.

(Bắt đầu 280Z28) @Justin: Tại sao không sử dụng?

public void AddHandler<Handler>(Control c, string eventName, Handler d) { 
    c.GetType().GetEvent(eventName).AddEventHandler(c, d as Delegate); 
} 

(End 280Z28)

+1

@Justin: Tôi đã chỉnh sửa câu trả lời của bạn để đưa nhận xét của tôi vào cuối vì nó có khối mã. –

+1

Bắt tốt. Điều đó hoạt động rất độc đáo. –

10

Nếu bạn sẵn sàng để có một sự phụ thuộc thời gian biên dịch trên một IL Weaver bạn có thể làm điều này với Fody.

Sử dụng addin này để Fody https://github.com/Fody/ExtraConstraints

Mã của bạn có thể trông như thế này

public class Sample 
{ 
    public void MethodWithDelegateConstraint<[DelegateConstraint] T>() 
    {   
    } 
    public void MethodWithEnumConstraint<[EnumConstraint] T>() 
    { 
    } 
} 

Và được biên dịch này

public class Sample 
{ 
    public void MethodWithDelegateConstraint<T>() where T: Delegate 
    { 
    } 

    public void MethodWithEnumConstraint<T>() where T: struct, Enum 
    { 
    } 
} 
+0

Liên kết bị hỏng. Bạn có một cái hiện tại? –

+0

@JustinMorgan đã cập nhật – Simon

2

Như đã đề cập ở trên, bạn không thể có Đại biểu và Enum như một ràng buộc chung. System.ObjectSystem.ValueType cũng không thể được sử dụng như một ràng buộc chung.

Công việc xung quanh có thể là nếu bạn xây dựng một cuộc gọi thích hợp trong bạn IL. Nó sẽ hoạt động tốt.

Đây là một ví dụ điển hình của Jon Skeet.

http://code.google.com/p/unconstrained-melody/

tôi đã lấy tài liệu tham khảo của tôi từ Jon Skeet cuốn sách C# trong Dept 3 phiên bản.

1

Theo MSDN

Lỗi trình biên dịch CS0702

Hạn chế không thể đặc biệt lớp 'nhận dạng' Các loại sau đây có thể không được sử dụng như những hạn chế:

  • System.Object
  • System.Array
  • System.Delegate
  • System.Enum
  • System.ValueType.
Các vấn đề liên quan