2009-02-26 34 views
23

Có lớp học chung sau đó sẽ chứa hoặc string, int, float, long như các loại:C#: Khai báo và sử dụng danh sách các lớp chung với các kiểu khác nhau, làm thế nào?

public class MyData<T> 
{ 
    private T _data; 

    public MyData (T value) 
    { 
     _data = value; 
    } 

    public T Data { get { return _data; } } 
} 

tôi đang cố gắng để có được một danh sách các MyData<T> nơi mỗi mục sẽ khác nhau T.

Tôi muốn để có thể truy cập vào một mục từ danh sách và nhận được giá trị của nó như trong đoạn mã sau:

MyData<> myData = _myList[0]; // Could be <string>, <int>, ... 
SomeMethod (myData.Data); 

nơi SomeMethod() được khai báo như sau:

public void SomeMethod (string value); 
public void SomeMethod (int value); 
public void SomeMethod (float value); 

CẬP NHẬT:

SomeMethod() là từ một cấp lớp khác mà tôi không có quyền kiểm soát và SomeMethod(object) không tồn tại.


Tuy nhiên, tôi dường như không thể tìm cách làm cho trình biên dịch hài lòng.

Mọi đề xuất?

Cảm ơn bạn.

+0

có thể xây dựng trên những gì SomeMethod của bạn không? Chúng tôi có thể giúp bạn tốt hơn. –

+0

Những gì SomeMethod không có liên quan ở đây. Tôi sẽ chỉ thêm rằng không có định nghĩa cho SomeMethod (đối tượng). –

+0

Thiết kế cơ bản của bạn bị thiếu sót. Tôi khuyên bạn nên đặt câu hỏi nơi bạn nêu chi tiết các yêu cầu và giải pháp của bạn và yêu cầu đề xuất. – Will

Trả lời

8

Các đại biểu thực sự có thể giúp đơn giản hóa này, và vẫn giữ cho mọi thứ kiểu an:

public void TestMethod1() 
{ 
    Action<SomeClass, int> intInvoke = (o, data) => o.SomeMethod(data); 
    Action<SomeClass, string> stringInvoke = (o, data) => o.SomeMethod(data); 

    var list = new List<MyData> 
    { 
     new MyData<int> { Data = 10, OnTypedInvoke = intInvoke }, 
     new MyData<string> { Data = "abc", OnTypedInvoke = stringInvoke } 
    }; 

    var someClass = new SomeClass(); 
    foreach (var item in list) 
    { 
     item.OnInvoke(someClass); 
    } 
} 

public abstract class MyData 
{ 
    public Action<SomeClass> OnInvoke; 
} 

public class MyData<T> : MyData 
{ 
    public T Data { get; set; } 
    public Action<SomeClass, T> OnTypedInvoke 
    { set { OnInvoke = (o) => { value(o, Data); }; } } 
} 

public class SomeClass 
{ 
    public void SomeMethod(string data) 
    { 
     Console.WriteLine("string: {0}", data); 
    } 

    public void SomeMethod(int data) 
    { 
     Console.WriteLine("int: {0}", data); 
    } 
} 
+0

Tuyệt vời mở mắt. – Telemat

0

Thừa kế MyData<T> từ lớp không chung chung MyData và tạo danh sách điều đó.

Bằng cách này, bạn không thể tự động giải quyết quá tải. Bạn phải làm điều đó bằng tay.

abstract class MyData { 
    protected abstract object GetData(); 
    protected abstract Type GetDataType(); 
    public object Data { 
     get { return GetData(); } 
    } 
    public Type DataType { 
     get { return GetDataType(); } 
    } 
} 

class MyData<T> : MyData { 
    protected override object GetData() { return Data; } 
    protected override Type GetDataType() { return typeof(T); } 
    public new T Data { 
    get { ... } 
    } 
} 
+0

Ok, bây giờ tôi sẽ sử dụng nó như: Danh sách danh sách = danh sách mới (); list.Add (myData mới ()); list.Add (new MyData ()); SomeMethod (danh sách [0] .Data); Bằng cách nào đó, tôi sẽ cần phải có SomeMethod() chấp nhận một đối tượng. Đây không phải là điều tôi không may. Cảm ơn bạn đã trả lời. Stecy –

+0

Vâng, đó là lý do tại sao tôi cho biết bạn nên quản lý quá tải bằng cách xem xét loại. Không có cách trực tiếp nào được hỗ trợ bởi generics. –

1

Trong trường hợp đó, bạn cần MyData<object> vì đó là điều duy nhất có những điểm chung.

+2

Làm thế nào để MyData cho phép gọi SomeMethod (int)? –

0

Generics cho phép bạn chỉ định một loại cho toàn bộ danh sách khi bạn tạo danh sách, ví dụ như một danh sách để lưu trữ int sẽ được tạo ra như thế này

var myData = new MyData<int>(); 

Nếu bạn muốn lưu trữ nhiều loại trong cùng một danh sách chung, bạn có thể chỉ định một loại cơ sở chung hoặc giao diện cho các loại đó. Thật không may trong trường hợp của bạn loại cơ sở chung duy nhất cho các loại bạn muốn lưu trữ sẽ là đối tượng.

var myData = new MyData<object>(); 

Nhưng bạn chỉ có thể sử dụng danh sách không chung chung để lưu trữ các đối tượng.

6

Chỉ cần sử dụng ArrayList và quên loại MyData<T>.

ArrayList myStuff = getStuff(); 
float x = myStuff.OfType<float>().First(); 
SomeMethod(x); 
string s = myStuff.OfType<string>().First(); 
SomeMethod(s); 

Sự cố với trình biên dịch để kiểm tra loại chỉ được biết khi chạy. Các trình biên dịch kiểm tra các kiểu được biết tại thời gian biên dịch.

+0

Cảm ơn! Đây là câu trả lời cho vấn đề cá nhân của tôi. Đã cố gắng theo dõi danh sách BlockingCollection và sau đó kiểm tra qua GetQueue trong đó T: Phương thức giao diện có hoặc không có hàng đợi kiểu T đã tồn tại trong danh sách của tôi và nếu không, khởi tạo và trả về hàng đợi trống mới. Nhưng BlockingCollection có thể không được ngầm đúc vào BlockingCollection , thậm chí nghĩ T: Giao diện, và tôi không thể cho cuộc sống của tôi tìm thấy chính xác covariant/contravariant/delegate thực hiện để làm điều này – Isaac

3

Bạn không thể thực hiện theo cách mình muốn.

Khi một cá thể của một lớp chung được khởi tạo, nó được gắn với loại cụ thể. Vì bạn muốn giữ các đối tượng của các kiểu khác nhau trong danh sách của mình, bạn phải tạo một cá thể ràng buộc với mẫu số chung ít nhất - trong trường hợp của bạn là Object.

Tuy nhiên, điều đó có nghĩa là thuộc tính Dữ liệu hiện sẽ trả về một đối tượng kiểu Object. Trình biên dịch không thể suy ra kiểu dữ liệu thực tế tại thời gian biên dịch, vì vậy nó có thể chọn quá tải SomeMethod thích hợp.

Bạn phải cung cấp quá tải SomeMethod lấy Object làm tham số hoặc xóa yêu cầu giữ các loại khác nhau như vậy trong bộ sưu tập của bạn.

Hoặc bạn có thể sử dụng bộ sưu tập tiêu chuẩn IEnumerable (như Array) và sử dụng phương thức mở rộng OfType<> để nhận tập con của tập hợp loại cụ thể.

+0

Tôi sợ nó sẽ giải quyết để sử dụng một đối tượng. .. Tuy nhiên, tôi không thể sử dụng một đối tượng vì SomeMethod là từ thư viện và tôi không thể thay đổi thực tế là nó sẽ không chấp nhận một đối tượng ... Ngoài ra, tôi không muốn chuyển () về loại biến ... –

1

Ký tự đại diện được đề xuất trong khi quay lại here. Bị đóng là "không khắc phục": (

13

Tôi nghĩ rằng vấn đề bạn đang gặp phải là vì bạn đang cố gắng tạo một loại chung, và sau đó tạo danh sách loại chung đó. đang cố gắng thực hiện bằng cách kết hợp các kiểu dữ liệu mà bạn đang cố gắng hỗ trợ, nói như là một phần tử IData, và sau đó tạo ra MyData generic của bạn với một ràng buộc của IData. kiểu dữ liệu để đại diện cho tất cả các kiểu dữ liệu nguyên thủy bạn đang sử dụng (string, int, float, dài) Nó có thể giống như thế này:.

public class MyData<T, C> 
    where T : IData<C> 
{ 
    public T Data { get; private set; } 

    public MyData (T value) 
    { 
     Data = value; 
    } 
} 

public interface IData<T> 
{ 
    T Data { get; set; } 
    void SomeMethod(); 
} 

//you'll need one of these for each data type you wish to support 
public class MyString: IData<string> 
{ 
    public MyString(String value) 
    { 
     Data = value; 
    } 

    public void SomeMethod() 
    { 
     //code here that uses _data... 
     Console.WriteLine(Data); 
    } 

    public string Data { get; set; } 
} 

và sau đó bạn thực hiện sẽ là một cái gì đó như:

var myData = new MyData<MyString, string>(new MyString("new string"));  
// Could be MyString, MyInt, ... 
myData.Data.SomeMethod(); 

nó hoạt động nhiều hơn một chút nhưng bạn có được chức năng mà bạn đang thực hiện.

UPDATE: remove SomeMethod từ giao diện của bạn và chỉ cần làm điều này

SomeMethod(myData.Data.Data); 
+0

Quá tệ Tôi cần bọc các loại nguyên thủy nhưng đó là điều tôi sẵn sàng chấp nhận vì không có cách nào khác. Tuy nhiên, trong ví dụ của bạn, bạn cho rằng SomeMethod() là một phần của MyData không phải là trường hợp (tôi đã cập nhật câu hỏi). –

+0

thực sự bạn có thể thực hiện cùng một điều, tôi chỉ gói nó để nó sẽ được đóng gói nhiều hơn, xem cập nhật của tôi – Joseph

+0

Vì vậy ... làm thế nào để một nơi MyData và MyData vào cùng một bộ sưu tập? –

1

Bạn có thể tạo một wrapper chung cho SomeMethod và kiểm tra các loại lập luận chung chung, sau đó uỷ thác cho phương pháp thích hợp.

public void SomeMethod<T>(T value) 
{ 
    Type type = typeof(T); 

    if (type == typeof(int)) 
    { 
     SomeMethod((int) (object) value); // sadly we must box it... 
    } 
    else if (type == typeof(float)) 
    { 
     SomeMethod((float) (object) value); 
    } 
    else if (type == typeof(string)) 
    { 
     SomeMethod((string) (object) value); 
    } 
    else 
    { 
     throw new NotSupportedException(
      "SomeMethod is not supported for objects of type " + type); 
    } 
} 
+0

Cảm ơn câu trả lời của bạn. Tôi e rằng tôi sẽ phải sử dụng quyền anh/unboxing sau khi tất cả. Tôi nghĩ rằng phải có một cách hiệu quả hơn .. –

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