2013-04-18 36 views
15

Nếu tôi có một lớp mục chung mà trông như thế này:C# Generics: Đơn giản hóa kiểu chữ ký

abstract class Item<T> 
{ 
} 

Và một container của mục đó trông như thế này:

class Container<TItem, T> 
    where TItem : Item<T> 
{ 
} 

Kể từ TItem phụ thuộc vào T , nó có thể đơn giản hóa chữ ký loại của Container để nó chỉ mất một tham số kiểu?

class Container<TItem> 
    where TItem : Item // this doesn't actually work, because Item takes a type parameter 
{ 
} 

Vì vậy, tôi có thể nhanh chóng nó như sau::

class StringItem : Item<string> 
{ 
} 

var good = new Container<StringItem>(); 
var bad = new Container<StringItem, string>(); 

Trình biên dịch sẽ có thể suy ra rằng T là chuỗi khi TItem là StringItem, phải một cái gì đó này những gì tôi thực sự muốn là? Làm thế nào để tôi thực hiện điều này?

mong muốn sử dụng:

class MyItem : Item<string> 
{ 
} 

Container<MyItem> container = GetContainer(); 
MyItem item = container.GetItem(0); 
item.MyMethod(); 
+0

Tôi nghĩ rằng bạn có thể. Đa hình của nó. Bạn có thể ghi đè lên các phương thức trừu tượng. –

+0

Trong trường hợp đó, bạn có thể tạo lớp StringContainer là lớp StringContainer trong đó TItem: Mục RockWorld

+0

Tôi không tin rằng trình biên dịch có thể _partially_ suy ra các loại chung chung. –

Trả lời

2

này nên làm những gì bạn muốn tôi nghĩ. Rõ ràng bây giờ bạn đang làm Container<string> không phải Container<StringItem> nhưng vì bạn đã không bao gồm các ví dụ sử dụng, tôi không thể thấy nó là một vấn đề.

using System.Collections.Generic; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var myContainer = new Container<string>(); 

      myContainer.MyItems = new List<Item<string>>(); 
     } 
    } 

    public class Item<T> { } 

    public class Container<T> 
    { 
     // Just some property on your container to show you can use Item<T> 
     public List<Item<T>> MyItems { get; set; } 
    } 
} 

Làm thế nào về phiên bản sửa đổi này:

using System.Collections.Generic; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var myContainer = new Container<StringItem>(); 

      myContainer.StronglyTypedItem = new StringItem(); 
     } 
    } 

    public class Item<T> { } 

    public class StringItem : Item<string> { } 

    // Probably a way to hide this, but can't figure it out now 
    // (needs to be public because it's a base type) 
    // Probably involves making a container (or 3rd class??) 
    // wrap a private container, not inherit it 
    public class PrivateContainer<TItem, T> where TItem : Item<T> { } 

    // Public interface 
    public class Container<T> : PrivateContainer<Item<T>, T> 
    { 
     // Just some property on your container to show you can use Item<T> 
     public T StronglyTypedItem { get; set; } 
    } 
} 
+0

Biên dịch này, nhưng tôi thực sự cần Container , không phải là Container . – brianberns

+2

@brianberns: Tại sao? (Không phàn nàn, sẽ hữu ích cho các câu trả lời tiềm năng) –

+0

Việc sử dụng trở nên phức tạp, nhưng hãy tưởng tượng rằng có một phương thức trên Container được gọi là GetItem (int index) phải trả về một TItem, không phải là một mục . Điều này cho phép người gọi gọi trực tiếp các phương thức TItem trên kết quả, mà không cần phải đưa nó từ mục đến TItem. – brianberns

1

Tôi nghĩ một giải pháp khả thi cho vấn đề của bạn là thêm giao diện IItem và cấu trúc mã sẽ như thế nào sau đây.

interface IItem { } 

abstract class Item<T> : IItem { } 

class Container<TItem> where TItem : IItem { } 

class StringItem: Item<string> { } 

Và bây giờ bạn có thể có Container<StringItem>:

var container = new Container<StringItem>(); 
+0

Đề xuất tốt, nhưng vấn đề với điều này là Container thực sự cần biết về thông số T tiềm ẩn của TItem. – brianberns

+0

Sau đó, nó là không thể. – hazzik

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