2010-06-15 31 views
8

Tôi có một tình huống mà tôi có một lớpGiao diện cho phương thức trả về kiểu riêng của mình

class Foo 
{ 
    Foo Bar() 
    { 
     return new Foo(); 
    } 
} 

Bây giờ tôi wan tot tạo một giao diện cho nó

class IFoo 
{ 
    ??? Bar(); 
} 

gì nên ở vị trí của dấu chấm hỏi? Mỗi lớp phải trả về kiểu riêng của nó, không phải Foo.

Các giải pháp dưới đây hoạt động nhưng không có vẻ sạch sẽ. Tôi không hiểu tại sao tôi phải chỉ định cùng lớp hai lần, và không có gì giống như "này" cho các loại hình hiện tại

Đây là cách tôi đang sử dụng nó sau này

class GenericClass<T> where T : IFoo 
{ 
    T foo = new T(); 
    T item = foo.Bar(); 
} 
+1

Vấn đề với giao diện chung là bạn cần chỉ định T bất cứ nơi nào bạn sử dụng giao diện này trong mã - vì vậy bạn đã ghép nối giao diện của mình với loại cơ bản. Giao diện có xu hướng thử và làm ngược lại? –

+0

@Andrey - Giống như Adam đã nói, đây là một mùi mã nghiêm trọng. Câu trả lời của tôi sẽ làm những gì bạn đang theo sau, nhưng nó không phải là thực hành tốt. – GenericTypeTea

+0

@GenericTypeTea, xin lỗi tôi nên làm rõ rằng nó sẽ làm việc :) –

Trả lời

9

Bạn hỏi:

Các giải pháp bel ow làm việc nhưng không trông sạch sẽ. Tôi không hiểu tại sao tôi phải chỉ định cùng một lớp hai lần và không có gì giống như "này" cho loại hiện tại

Lý do tại sao bạn phải chỉ định hai lần là vì C# thiếu tính năng mà bạn nhu cầu. gì bạn muốn là một cái gì đó như thế này:

interface IFoo 
{ 
    IFoo Bar(); 
} 

class Foo : IFoo 
{ 
    Foo Bar() // should work since Foo is an IFoo, but it's not supported by C# 
    { 
     return new Foo(); 
    } 
} 

Từ một điểm kiểu an toàn, điều này nên làm việc (nó được gọi là return type covariance). Trên thực tế, các ngôn ngữ lập trình khác như C++ hoặc Java hỗ trợ điều này, xem this example on Wikipedia. Thật không may, phương thức trả về kiểu trả về không được hỗ trợ bởi C# (thậm chí không C# 4.0, mà giới thiệu hiệp phương sai cho generics), đó là lý do tại sao bạn phải sử dụng "workaround generics" minh họa trong các câu trả lời khác.

kiểu trả về hiệp biến cũng như một "này" loại được tính năng đề xuất cho phiên bản mới của C#:

+0

Mã của bạn sẽ không biên dịch vì lớp Foo không thực hiện 'IFoo Bar()'. Bạn có thể sửa đổi Foo này để làm cho Bar trả về một IFoo, nhưng nó có nghĩa là bạn cần upcast IFoo đến Foo ở bất cứ đâu bạn có thể muốn truy cập các phương thức của Foo. (mặc dù an toàn để làm, không phải rất gọn gàng để viết).
Hiệp phương sai và contravariance được hỗ trợ trong các loại chung như C# 4.0, nhưng chúng không thực sự cần thiết cho kịch bản này. Giải pháp GenerticTypeTea được đăng là đủ. –

+0

@Mark H - Tôi nghĩ những gì Heinzi đang nói là "đây là những gì OP muốn ... tuy nhiên nó không thể". – GenericTypeTea

+0

@GenericTypeTea: Chính xác. :-) Trên thực tế, tôi đang nói "Không thể trong C# (nhưng bằng các ngôn ngữ khác)." – Heinzi

8

Bạn có thể thêm một kiểu generic và hạn chế nó bằng cách sử dụng loại giao diện:

public interface IFoo<T> 
{ 
    T Bar(); 
} 

Bạn muốn thực hiện điều này như sau:

public class Foo : IFoo<Foo> 
{ 
    public Foo Bar() 
    { 
     return new Foo(); 
    } 
} 

public class Cheese : IFoo<Cheese> 
{ 
    public Cheese Bar() 
    { 
     return new Cheese(); 
    } 
} 

Cập nhật, nếu bạn không bao giờ quan tâm đến kiểu trả về bê tông của Foo, sau đó bạn có thể làm như sau:

public interface IFoo 
{ 
    IFoo Bar(); 
} 

nào được thực hiện như:

public class Foo : IFoo 
{ 
    public IFoo Bar() 
    { 
     return new Foo(); 
    } 
} 

Sau đó, trong lớp generic của bạn:

public class GenericClass<T> where T : class, IFoo, new() 
{ 
    public T Rar() 
    { 
     T foo = new T(); 
     T item = foo.Bar() as T; 
     return item; 
    } 
} 

GenericClass<Foo>.Rar(); sẽ thực hiện cụ thể Foo.

+0

Điều này sẽ không biên dịch. – SLaks

+0

@SLaks: Anh ấy chỉ cần thêm 'công khai', đúng không? –

+0

Có vẻ ổn với tôi. Tại sao nó không nên biên dịch? – jalf

0
public interface IFoo<T> 
{ 
    T Bar(); 
} 

thực hiện của bạn sau đó sẽ là:

class Foo : IFoo<Foo> 
{ 
    Foo Bar() 
    { 
     return new Foo(); 
    } 
} 

class Baz : IFoo<Baz> 
{ 
    Baz Bar() 
    { 
     return new Baz(); 
    } 
} 
0

Bạn cần phải thực hiện giao diện chung chung, như thế này:

interface IFoo<TClass> where TClass : IFoo<TClass>, class { 
    TClass Bar(); 
} 
2

Bạn có thể sử dụng một lớp cơ sở trừu tượng cộng với thực hiện thành viên rõ ràng để đạt được điều này. Thứ nhất, tuyên bố giao diện của bạn như thế này:

interface IFoo 
{ 
    IFoo Bar(); 
} 

Sau đó, khai báo một lớp trừu tượng chung chung mà thực hiện IFoo một cách rõ ràng, và cũng có thể tuyên bố một phương pháp trừu tượng mà loại "quá tải" Bar(), nhưng trong một generic cách:

abstract class BaseFooImpl<T> : IFoo where T : BaseFooImpl 
{ 
    public abstract T Bar(); 

    IFoo IFoo.Bar() 
    { 
     return Bar(); // this will call the abstract Bar() 
    } 
} 

Bây giờ, định nghĩa các lớp bê tông của bạn như thế này:

class ConcreteFoo : BaseFooImpl<ConcreteFoo> 
{ 
    public override ConcreteFoo Bar() 
    { 
     return this; // for example, of course. 
    } 
} 

Ưu điểm của phương pháp này là bạn luôn có thể sử dụng tham khảo IFoo không chung chung để giữ các thể hiện cụ thể.Nếu bạn thực hiện giao diện của bạn chung chung, bạn có thể không, ví dụ, tuyên bố sau đây:

IFoo mammalInstance, fishInstance; // Instead of IFoo<Mammal> mammalInstance; IFoo<Fish> fishInstance; 
List<IFoo> manyInstances; // Instead of List<IFoo<IFoo>>, which doesn't even work AFAIK 
+0

Và điều gì ngăn cản ai đó nói lớp C2: BaseFooImpl ? Không có gì. Điều này không hạn chế Bar trả về C2. –

+0

@Eric, thật đáng lo ngại. Nó thậm chí còn làm mất hiệu lực câu trả lời của tôi, nếu chúng ta tuân theo các nhu cầu của OP. Tôi đoán vấn đề này là phổ biến cho các giải pháp khác được đăng ở đây. Tuy nhiên, 'C2' (hoặc tốt hơn, tác giả của nó) biết nó đang làm gì. – Humberto

+0

@Eric IS có cách nào để hạn chế thứ gì đó để trả về kiểu riêng của nó bằng cách sử dụng Giao diện không? Ngay cả "ICloneable" chỉ hạn chế coder trả về 'object'. Tôi có thể dễ dàng có 'Apple' trả về' Orange' – DevinB

0

Không chắc chắn những gì bạn đang cố gắng để hoàn thành nhưng nó có thể được thực hiện theo cách này:

interface IFoo<T> 
{ 
    T Bar(); 
} 



    class Foo:IFoo<Foo> 
    { 

     #region IFoo<Foo> Members 

     public Foo Bar() 
     { 
      return new Foo(); 
     } 

     #endregion 
    } 

Hoặc Giống như này:

interface IFoo 
    { 
     IFoo Bar(); 
    } 

class Foo : IFoo 
    { 

     #region IFoo Members 

     public IFoo Bar() 
     { 
      return new Foo(); 
     } 

     #endregion 
    } 
4

Tôi nghĩ rằng câu hỏi thực sự là: tại sao bạn cần loại có nguồn gốc trong giao diện? Giao diện chính xác vì lý do đó - trừu tượng hóa từ các lớp cụ thể. Nếu nó chỉ thuận tiện, vì vậy bạn không phải truyền đến Foo sau khi gọi cho Bar(), bạn có thể triển khai giao diện rõ ràng:

Tự hỏi mình câu hỏi: tại sao bạn giới thiệu giao diện khi bạn muốn loại bê tông?

+0

+1 Đây chính xác là những gì tôi đang nghĩ. – juharr

+0

Để đưa ra một ví dụ đơn giản: Đối tượng này sau đó được sử dụng để liên kết một lưới. Tại thời điểm đó tôi không sử dụng giao diện chỉ bao gồm các crud, nhưng thay vào đó phụ thuộc vào các thuộc tính đại diện cho cột – Andrey

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