2012-06-29 28 views
9

tôi nghi ngờ câu trả lời là không, nhưng tôi muốn biết nếu nó có thể làm điều gì đó như thế này:Có thể hạn chế một tham số kiểu phương thức chung của C# như "có thể gán từ" tham số kiểu của lớp có chứa không?

public class MyGenericClass<TSomeClass> { 
    public void MyGenericMethod<TSomeInterface>() 
     // This doesn't compile. 
     where TSomeClass : TSomeInterface 
    { 
     //... 
    } 
} 

Những gì tôi có nghĩa là để chỉ ra ở trên ví dụ (không làm việc) là để hạn chế TSomeInterface như vậy rằng nó có thể là bất kỳ lớp cơ sở, giao diện thực hiện, hoặc (nếu bạn thực sự muốn có được chuyển đổi tiềm ẩn) tiềm ẩn của MyGenericClass.

LƯU Ý: tôi nghi ngờ rằng lý do tại sao điều này không bao giờ được thực hiện trong C# là trở ngại chung là không thực sự có nghĩa là hợp đồng mã, mà là cách tôi đang cố gắng sử dụng chúng ở đây. Tôi thực sự không quan tâm loại TSomeInterface là gì, miễn là nó được triển khai bởi TSomeClass.

Cho đến nay, tôi đã bị hack này với nhau:

public class MyGenericClass<TSomeClass> { 
    public void MyGenericMethod<TIntermediateType, TSomeInterface>() 
     where TIntermediateType : TSomeClass, TSomeInterface 
    { 
     //... 
    } 
} 

này nhiều hay ít thực thi các hạn chế mà tôi muốn (mà TSomeClass phải kế thừa từ, hoặc trong trường hợp của một giao diện, thực hiện, TSomeInterface), nhưng gọi nó là rất vụng về, vì tôi phải chỉ rõ TIntermediateType (mặc dù tôi thực sự muốn nó để đánh giá đối với TSomeClass):

var myGenericInstance = new MyGenericClass<TSomeClass>(); 
myGenericInstance.MyGenericMethod(TSomeClass, TSomeInterface); 

Bên cạnh đó, các xe cho thuê ở trên bị phá vỡ vì một người gọi có thể trong ory chỉ định một lớp con của TSomeClass làm thông số loại đầu tiên, nơi chỉ có lớp con triển khai TSomeInterface.

Lý do mà tôi muốn làm này là tôi đang viết một mô hình nhà máy thông thạo cho một dịch vụ WCF, và tôi sẽ như để ngăn chặn người gọi (tại thời gian biên dịch) từ cố gắng để tạo ra một thiết bị đầu cuối với một hợp đồng mà lớp dịch vụ không thực hiện. Tôi rõ ràng có thể kiểm tra này lúc chạy (WCF trong thực tế hiện điều này cho tôi), nhưng tôi là một fan hâm mộ lớn của kiểm tra thời gian biên dịch.

Có cách nào tốt hơn/thanh lịch hơn để đạt được những gì tôi ở đây không?

+1

Điều này thực tế là không thể vì bạn đang cố gắng kết hợp các generics với thời gian chạy. – Polity

+0

Không, tôi không có. Tôi chỉ cố gắng áp dụng một ràng buộc chung (theo như tôi có thể nói) ngôn ngữ C# không cho phép (ràng buộc một tham số kiểu sao cho nó là một siêu lớp hoặc giao diện thực hiện của một tham số kiểu trong lớp chứa). –

+0

Theo tôi thấy, bạn cố gắng hạn chế lớp chung dựa trên loại dữ liệu bạn nạp vào một phương thức.Nếu bạn muốn hạn chế lớp chung của mình, bạn cần phải làm điều này ở cấp độ lớp (logic), không có cách nào để trình biên dịch JIT tạo ra một lớp cụ thể. – Polity

Trả lời

3

Con đường tôi đã có thể quấn quanh đầu tôi lý do tại sao điều này không biên dịch như sau:

xem xét chương trình này biên dịch:

class Program { 
    class Class1 { } 
    class Class2 { } 
    public class MyGenericClass<TSomeClass> { 
     public void MyGenericMethod<TSomeInterface>() where TSomeClass : TSomeInterface { 
     } 
    } 
    static void Main(string[] args) { 
     var inst = new MyGenericClass<Class1>(); 
    } 
} 

Mọi thứ đều tốt. Trình biên dịch rất vui. Bây giờ xem xét để thay đổi phương pháp Main:

static void Main(string[] args) { 
    var inst = new MyGenericClass<Class1>(); 
    inst.MyGenericMethod<Class2>(); 
} 

Trình biên dịch sẽ phàn nàn rằng Class1 không thực hiện Class2. Nhưng dòng nào là sai? Ràng buộc là trên cuộc gọi đến MyGenericMethod, nhưng dòng mã vi phạm là việc tạo ra MyGenericClass.

Nói cách khác, cái nào có đường kẻ màu đỏ?

+1

Trong thế giới lý tưởng của tôi nó sẽ là phương thức gọi tất nhiên, cũng giống như nó sẽ là nếu bạn cố gắng làm điều này: 'var x = new List (); s.Add (5);'. Trình biên dịch giả định khai báo là đúng và cuộc gọi phương thức đang sử dụng đối số không hợp lệ. +1 cho suy nghĩ về cú pháp. –

3

Như được thảo luận trong this linked question, bạn không thể sử dụng thông số loại không thuộc khai báo hiện tại, ở bên trái của mệnh đề where.

Vì vậy, theo đề nghị của w0lf trong đó câu hỏi khác, những gì bạn có thể làm là cung cấp cả hai loại trong giao diện của bạn (chứ không phải là phương pháp) khai:

public class MyGenericClass<TSomeClass, TSomeInterface> { 
    where TSomeClass : TSomeInterface 
    public void MyGenericMethod() // not so generic anymore :( 
    { 
     //... 
    } 
} 

đó, tuy nhiên, rất nhiều hạn chế MyGenericMethod của bạn và buộc bạn lớp để khai báo trước giao diện cơ bản nào bạn cho phép.

Vì vậy một lựa chọn là sử dụng một phương pháp tĩnh với các thông số loại hơn:

public class MyGenericClass<TSomeClass> { 
    public static void MyGenericMethod<TSomeClass, TSomeInterface> 
             (MyGenericClass<TSomeClass> that) 
     where TSomeClass : TSomeInterface 
    { 
     // use "that" instead of this 
    } 
} 

Có thể bạn có thể làm cho nó trở thành một phương pháp khuyến nông để làm cho nó xuất hiện cho người dùng như một phương pháp thực tế.

Không phải trong số này là chính xác những gì bạn muốn, nhưng có lẽ tốt hơn so với giải pháp loại trung gian.

Vì lý do cho tại sao không?, tôi đoán là nó sẽ làm phức tạp trình biên dịch mà không thêm đủ giá trị. Đây là một số discussion by Angelika Langer of the same subject but about Java. Mặc dù có sự khác biệt đáng kể giữa C# và Java, tôi nghĩ rằng kết luận của mình có thể áp dụng ở đây cũng như:

Điểm mấu chốt là tính hữu ích của giới hạn thấp hơn trên loại thông số có phần gây tranh cãi. Họ sẽ gây nhầm lẫn và có lẽ thậm chí gây hiểu nhầm khi được sử dụng làm thông số loại của một lớp chung chung. Mặt khác, trên , các phương pháp chung thường có lợi nhuận từ thông số loại với giới hạn dưới. Đối với các phương pháp, có thể tìm thấy thông số xung quanh thiếu thông số loại giới hạn dưới. Công việc như vậy thường bao gồm một phương pháp chung chung tĩnh hoặc một ký tự đại diện bị ràng buộc thấp hơn.

Cô ấy cũng cung cấp trường hợp sử dụng tốt, xem liên kết ở trên.

1

Một phương pháp mở rộng cung cấp giải pháp tốt nhất, mặc dù nó không hoàn toàn giải quyết tất cả các mối quan tâm của bạn.

public class MyGenericClass<TSomeClass> 
{ 
} 

public static class MyGenericClassExtensions 
{ 
    public static void MyGenericMethod<TSomeClass, TSomeInterface>(this MyGenericClass<TSomeClass> self) 
     where TSomeClass : TSomeInterface 
    { 
     //... 
    } 
} 

Nó vẫn còn cần thiết để xác định cả hai loại khi gọi MyGenericMethod, nhưng nó ngăn chặn người gọi từ chỉ định một loại không chính xác cho TSomeClass càng tốt với phương pháp bạn đã đưa ra. Với phương pháp này, phương pháp này có thể được gọi như vậy:

var myGenericInstance = new MyGenericClass<TSomeClass>(); 
myGenericInstance.MyGenericMethod<TSomeClass, TSomeInterface>(); 

Nó sẽ là một lỗi biên dịch nếu tham số kiểu MyGenericClass được khai báo với không phù hợp với thông số loại đầu tiên để MyGenericMethod.

Vì tham số kiểu đầu tiên có thể được suy ra theo đối số this, trình biên dịch có thể suy ra cả hai tham số kiểu nếu tham số bổ sung của chúng vào phương thức.

Các vấn đề liên quan