2011-11-11 19 views
8

Tôi đang viết một phương pháp tổng quát để sử dụng nó trong một nhiệm vụ đặc biệt ở mẫu T4. Phương pháp này cho phép tôi sử dụng các loại chuyên biệt từ một giao diện chung. Tôi nghĩ về chữ ký sau đây:Một phương pháp chung có thể sử dụng các loại contravariant/covariant?

interface IGreatInterface { 
    Object aMethodAlpha<U>(U parameter) where U : IAnInterface; 
    Object aMethodBeta(IAnInterface parameter) 
} 

public class AnInterestingClass : IAnInterface{} 

Khi tôi cố gắng thực hiện IGreatInterface cờ biên dịch một lỗi cho aMethodBeta() vì tôi đã thực hiện T4 của tôi để viết rằng phương pháp sử dụng một subtype của IAnInterface (tức là tôi muốn thực hiện điều đó phương pháp như sau: Object aMethodBeta(AnInterestingClass parameter)).

Phương pháp aMethodAlpha<U>() có thể được sử dụng nhưng không sạch như tôi muốn vì T4 của tôi phải tạo thêm một số mã. Tôi (có lẽ sai) đề xuất rằng việc triển khai phương pháp đó, mà phải được thực hiện bằng T4, có thể là
Object aMethodAlpha<AnInterestingClass>(AnInterestingClass parameter).

Tôi nghĩ rằng các phương pháp chung không hỗ trợ các loại contravariant nhưng tôi không chắc chắn; Tôi cho rằng đó là cách trình biên dịch ngăn cản trình mã hóa sử dụng một loại cụ thể có phương thức không được định nghĩa trong kiểu chung ...

  1. Phương pháp chung có sử dụng đúng loại khi được triển khai không?
  2. Có bất kỳ mẹo nào để thay đổi hành vi này không?
+0

Tôi không hiểu câu hỏi của bạn. Bạn có thể đăng mã thực hiện IGreatInterface và lỗi trình biên dịch cụ thể không? – phoog

+2

@Juan: Cũng giống như một lưu ý phụ giúp đánh dấu nội dung cập nhật của bạn bằng * cập nhật * hoặc thứ gì đó để chúng tôi thấy những gì đã thay đổi. –

Trả lời

20

Câu hỏi này là khá khó hiểu . Hãy để tôi xem nếu tôi có thể làm rõ nó.

Khi tôi cố gắng thực hiện IGreatInterface cờ biên dịch một lỗi cho aMethodBeta() vì tôi đã thực hiện rằng phương pháp sử dụng một subtype của IAnInterface Tôi muốn thực hiện phương pháp như thế này: Object aMethodBeta(AnInterestingClass parameter).

Điều đó không hợp pháp. Đơn giản hóa một phần:

class Food {} 
class Fruit : Food {} 
class Meat : Food {} 
interface IEater 
{ 
    void Eat(Food food); 
} 
class Vegetarian : IEater 
{ 
    public void Eat(Fruit fruit); 
} 

Lớp Vegetarian không hoàn thành hợp đồng IEater. Bạn sẽ có thể vượt qua bất kỳ Thực phẩm nào để ăn, nhưng chỉ Vegetarian mới chấp nhận trái cây. C# không hỗ trợ phương sai tham số chính thức của phương thức ảo vì đó không phải là an toàn.

Bây giờ, sau đó bạn có thể nói, như thế nào về vấn đề này:

interface IFruitEater 
{ 
    void Eat(Fruit fruit); 
} 
class Omnivore : IFruitEater 
{ 
    public void Eat(Food food); 
} 

Bây giờ chúng tôi đã có loại an toàn; Omnivore có thể được sử dụng như một IFruitEater vì một Omnivore có thể ăn trái cây, cũng như bất kỳ thực phẩm nào khác.

Thật không may, C# không hỗ trợ phương pháp ảo loại tham số chính thức contravariance mặc dù làm như vậy là trong các loại an toàn lý thuyết. Rất ít ngôn ngữ hỗ trợ điều này.

Tương tự, C# không hỗ trợ phương thức trả về phương thức ảo.

Tôi không chắc liệu điều đó có thực sự trả lời câu hỏi của bạn hay không. Bạn có thể làm rõ câu hỏi?

UPDATE:

gì về:

interface IEater 
{ 
    void Eat<T>(T t) where T : Food; 
} 
class Vegetarian : IEater 
{ 
    // I only want to eat fruit! 
    public void Eat<Fruit>(Fruit food) { } 
} 

Không, đó không phải là quy phạm pháp luật trong hai. Hợp đồng của IEater là bạn sẽ cung cấp phương thức Eat<T> có thể mất bất kỳ T nào là Food.Bạn có thể không phần thực hiện hợp đồng, bất kỳ hơn bạn có thể làm điều này:

interface IAdder 
{ 
    int Add(int x, int y); 
} 
class Adder : IAdder 
{ 
    // I only know how to add two! 
    public int Add(2, int y){ ... } 
} 

Tuy nhiên, bạn có thể làm điều này:

interface IEater<T> where T : Food 
{ 
    void Eat(T t); 
} 
class Vegetarian : IEater<Fruit> 
{ 
    public void Eat(Fruit fruit) { } 
} 

Đó là hoàn toàn hợp pháp. Tuy nhiên, bạn không thể làm:

interface IEater<T> where T : Food 
{ 
    void Eat(T t); 
} 
class Omnivore : IEater<Fruit> 
{ 
    public void Eat(Food food) { } 
} 

Bởi vì một lần nữa, C# không hỗ trợ phương thức ảo sai lệch hoặc hiệp phương sai.

Lưu ý rằng C# không hỗ trợ đa hình tham số hiệp phương sai khi làm như vậy được biết là an toàn. Ví dụ: điều này là hợp pháp:

IEnumerable<Fruit> fruit = whatever; 
IEnumerable<Food> food = fruit; 

Một chuỗi trái cây có thể được sử dụng như một chuỗi thức ăn. Hoặc,

IEnumerable<Fruit> fruitComparer = whatever; 
IComparable<Apples> appleComparer = fruitComparer; 

Nếu bạn có thứ gì đó có thể so sánh hai quả thì có thể so sánh hai quả táo bất kỳ.

Tuy nhiên, loại hiệp phương sai và đối nghịch này chỉ là hợp pháp khi tất cả những điều sau là đúng: (1) phương sai được chứng minh là an toàn, (2) tác giả của loại thêm chú thích phương sai cho thấy đồng và contra mong muốn -ngân số, (3) các kiểu đối số khác nhau có liên quan là tất cả các kiểu tham chiếu, (4) kiểu generic là một delegate hoặc một giao diện.

+0

(Câu trả lời tuyệt vời! Nhận xét này sẽ được sử dụng để làm rõ câu hỏi sau ...). Sử dụng ví dụ của bạn: tôi có thể tạo chữ ký 'IEater.Eat (T thực phẩm) trong đó T: Food' và sau đó triển khai' Omnivore.Eat (Thực phẩm trái cây) '? mà không có trình biên dịch gắn cờ tôi vì không thực hiện 'IEater.Ăn (T thực phẩm) '?. Từ câu trả lời của bạn, tôi nghĩ rằng điều đó là không thể ... – JPCF

+0

@JuanPabloContreras: Tôi đã thêm một số văn bản bổ sung liên quan đến nhận xét của bạn. –

+0

@EricLippert Nếu có thể, bạn có thể đưa ra suy nghĩ của mình về câu hỏi này http://stackoverflow.com/questions/8109478/process-address-space-vs-virtual-memory – Sandeep

2

Nếu bạn muốn kế thừa từ giao diện chung, hãy xem câu trả lời của phoog. Nếu bạn đang nói về việc cố gắng để thực hiện một giao diện đồng biến thể, dẫn đến thảo luận của tôi dưới đây.

Giả:

internal interface IAnInterface { } 

public class SomeSubClass : IAnInterface { } 

public class AnotherSubClass : IAnInterface { } 

public GreatClass : IGreatInterface { ... } 

Vấn đề với cố gắng để thực hiện các giao diện với một nguồn gốc hơn (đồng biến) đối số là không có guarante khi điều này được gọi là thông qua một giao diện mà một IAnInterface thông qua năm sẽ là một SomeSubClass bản sao. Đây là lý do tại sao số điện thoại không phải là được phép trực tiếp.

IGreatInterface x = new GreatClass(); 

x.aMethodBeta(new AnotherSubClass()); 

NẾU Bạn có thể làm hiệp phương sai, điều này sẽ thất bại bởi vì bạn sẽ được mong đợi một SomeSubClass nhưng sẽ nhận được một AnotherSubClass.

gì bạn thể làm là để làm giao diện thực hiện rõ ràng:

class GreatInterface : IGreatInterface 
{ 
    // explicitly implement aMethodBeta() when called from interface reference 
    object IGreatInterface.aMethodBeta(IAnInterface parameter) 
    { 
     // do whatever you'd do on IAnInterface itself... 
     var newParam = parameter as SomeSubClass; 

     if (newParam != null) 
     { 
      aMethodBeta(newParam); 
     } 

     // otherwise do some other action... 
    } 

    // This version is visible from the class reference itself and has the 
    // sub-class parameter 
    public object aMethodBeta(SomeSubClass parameter) 
    { 
     // do whatever 
    } 
} 

Vì vậy, nếu bạn đã làm điều này, giao diện của bạn hỗ trợ chung, lớp có một phương pháp cụ thể hơn, nhưng vẫn hỗ trợ giao diện . Sự khác biệt chính là bạn cần phải xử lý các trường hợp thực hiện một bất ngờ của IAnInterface được thông qua tại

CẬP NHẬT:. Có vẻ như bạn muốn một cái gì đó như thế này:

public interface ISomeInterface 
{ 
    void SomeMethod<A>(A someArgument); 
} 

public class SomeClass : ISomeInterface 
{ 
    public void SomeMethod<TA>(TA someArgument) where TA : SomeClass 
    { 

    } 
} 

này không được phép , khi bạn thực hiện một phương thức chung từ một giao diện, các ràng buộc phải phù hợp.

+0

hmmm ... từ câu trả lời của bạn, tôi có thể phỏng đoán rằng .net không chấp nhận các kiểu con với các phương thức chung ... Tôi có đúng không? – JPCF

+0

@Juan: Tôi không chắc chắn ý của bạn là "không chấp nhận". Bạn có ý là đối số? Là kiểu tham số? –

+0

@Juan: Nếu bạn có nghĩa là tôi có thể thực hiện một giao diện nhưng làm cho loại có nguồn gốc nhiều hơn loại giao diện? Không, bạn không thể. Bạn có thể đồng biến thể, contra-variantly pass/return args, và bạn có thể đồng biến thể, giao tiếp với nhau và gán các giao diện và đại biểu (nếu được đánh dấu một cách thích hợp), nhưng không thực hiện. –

0

Có lẽ bạn đang tìm kiếm này:

interface IGreatInterface<in U> where U : IAnInterface 
{ 
    Object aMethodAlpha(U parameter); 
} 

class SomeClass : IAnInterface { /*...*/ } 

class GreatClass : IGreatInterface<SomeClass> 
{ 
    public Object aMethodAlpha(SomeClass parameter) {} 
} 

EDIT:

Vâng, bạn là đúng: nếu bạn xác định một phương pháp chung trong một giao diện, bạn không thể thực hiện điều đó với một phương pháp phương pháp cụ thể bằng cách sử dụng một loại tương thích.

Làm thế nào về việc sử dụng một đại biểu (kể từ khi các đại biểu ủng hộ đồng và contravariance):

[ví dụ xóa bởi vì tôi có phương sai ngược - nó không hoạt động.]

+0

nope ... vui lòng đọc nội dung cập nhật ... – JPCF

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