C # - Nhiều loại chung trong một danh sách


153

Điều này có lẽ là không thể, nhưng tôi có lớp này:

public class Metadata<DataType> where DataType : struct
{
    private DataType mDataType;
}

Có nhiều hơn thế, nhưng hãy giữ nó đơn giản. Kiểu chung (DataType) được giới hạn ở các loại giá trị bởi câu lệnh where. Những gì tôi muốn làm là có một danh sách các đối tượng Siêu dữ liệu thuộc các loại khác nhau (DataType). Nhu la:

List<Metadata> metadataObjects;
metadataObjects.Add(new Metadata<int>());
metadataObjects.Add(new Metadata<bool>());
metadataObjects.Add(new Metadata<double>());

Điều này thậm chí có thể?


24
Tôi tự hỏi nếu có bất kỳ lợi ích thực sự cho các phương pháp tiếp cận trong các câu trả lời dưới đây so với chỉ sử dụng một List<object>? Họ sẽ không ngừng đấm bốc / bỏ hộp, họ sẽ không loại bỏ nhu cầu casting, và cuối cùng, bạn đang nhận được một Metadatađối tượng không cho bạn biết bất cứ điều gì về thực tế DataType, tôi đang tìm kiếm một giải pháp để giải quyết những vấn đề đó. Nếu bạn định khai báo một giao diện / lớp, chỉ vì mục đích có thể đặt loại chung chung thực hiện / dẫn xuất trong một danh sách chung, thì nó khác với việc sử dụng một lớp List<object>khác ngoài việc có một lớp vô nghĩa như thế nào?
Saeb Amini

9
Cả lớp cơ sở trừu tượng và giao diện đều cung cấp một mức độ kiểm soát bằng cách hạn chế loại phần tử có thể được thêm vào danh sách. Tôi cũng không thể thấy quyền anh đi vào đây.
0b101010

3
Tất nhiên, nếu bạn đang sử dụng .NET v4.0 trở lên thì hiệp phương sai là giải pháp. List<Metadata<object>>không lừa
0b101010

2
@ 0b101010 Tôi cũng nghĩ như vậy, nhưng tiếc là phương sai không được phép đối với các loại giá trị. Vì OP có một structràng buộc, nó không hoạt động ở đây. Xem
nawfal

@ 0b101010, Cả hai chỉ hạn chế các loại tham chiếu, mọi loại giá trị tích hợp và mọi cấu trúc vẫn có thể được thêm vào. Ngoài ra, cuối cùng, bạn có một danh sách các MetaDataloại tài liệu tham khảo thay vì các loại giá trị ban đầu của bạn không có (thời gian biên dịch) thông tin về kiểu giá trị cơ bản của mỗi yếu tố, đó là một cách hiệu quả "đấm bốc".
Saeb Amini

Câu trả lời:


195
public abstract class Metadata
{
}

// extend abstract Metadata class
public class Metadata<DataType> : Metadata where DataType : struct
{
    private DataType mDataType;
}

5
Ồ Tôi thực sự không nghĩ rằng điều đó là có thể! Bạn là một người cứu rỗi cuộc sống, anh bạn!
Carl

2
+10 cho điều này! Tôi không biết tại sao điều này biên dịch .. Chính xác những gì tôi cần!
Odys

Tôi có một vấn đề tương tự, nhưng lớp chung của tôi mở rộng từ một lớp chung khác, vì vậy tôi không thể sử dụng giải pháp của bạn ... có ý tưởng nào về cách khắc phục cho tình huống này không?
Sheridan

10
Có một lợi ích cho phương pháp này so với đơn giản List<object>? xin vui lòng xem bình luận của tôi được đăng dưới câu hỏi của OP.
Saeb Amini

11
@SaebAmini Danh sách <object> không hiển thị bất kỳ ý định nào đối với nhà phát triển, cũng như không ngăn nhà phát triển tự bắn vào chân mình bằng cách thêm nhầm một số đối tượng không phải là MetaData vào danh sách. Bằng cách sử dụng Danh sách <MetaData>, danh sách này nên chứa những gì. Nhiều khả năng MetaData sẽ có một số thuộc tính / phương thức công khai chưa được hiển thị trong các ví dụ trên. Truy cập những người thông qua đối tượng sẽ đòi hỏi một dàn diễn viên cồng kềnh.
Buzz

92

Theo câu trả lời của leppie, tại sao không tạo MetaDatagiao diện:

public interface IMetaData { }

public class Metadata<DataType> : IMetaData where DataType : struct
{
    private DataType mDataType;
}

Ai đó có thể cho tôi biết tại sao phương pháp này tốt hơn?
Lazlo

34
Bởi vì không có chức năng chung nào được chia sẻ - tại sao lại lãng phí một lớp cơ sở trên đó? Một giao diện là đủ
flq

2
Bởi vì bạn có thể thực hiện các giao diện trong struct.
Damian Leszczyński - Vash

2
Kế thừa lớp bằng các phương thức ảo, tuy nhiên, nhanh hơn khoảng 1,4 lần so với các phương thức giao diện. Vì vậy, nếu bạn có kế hoạch triển khai bất kỳ phương thức / thuộc tính MetaData (ảo) không chung nào trong MetaData <DataType>, hãy chọn một lớp trừu tượng thay vì giao diện, nếu hiệu suất là mối quan tâm. Nếu không, sử dụng một giao diện có thể linh hoạt hơn.
TamusJRoyce

30

Tôi cũng đã sử dụng một phiên bản không chung chung, sử dụng newtừ khóa:

public interface IMetadata
{
    Type DataType { get; }

    object Data { get; }
}

public interface IMetadata<TData> : IMetadata
{
    new TData Data { get; }
}

Triển khai giao diện rõ ràng được sử dụng để cho phép cả hai Datathành viên:

public class Metadata<TData> : IMetadata<TData>
{
    public Metadata(TData data)
    {
       Data = data;
    }

    public Type DataType
    {
        get { return typeof(TData); }
    }

    object IMetadata.Data
    {
        get { return Data; }
    }

    public TData Data { get; private set; }
}

Bạn có thể lấy được một loại giá trị nhắm mục tiêu phiên bản:

public interface IValueTypeMetadata : IMetadata
{

}

public interface IValueTypeMetadata<TData> : IMetadata<TData>, IValueTypeMetadata where TData : struct
{

}

public class ValueTypeMetadata<TData> : Metadata<TData>, IValueTypeMetadata<TData> where TData : struct
{
    public ValueTypeMetadata(TData data) : base(data)
    {}
}

Điều này có thể được mở rộng cho bất kỳ loại ràng buộc chung nào.


4
+1 chỉ vì bạn đang chỉ cho bạn cách sử dụng nó ( DataTypeobject Datađã giúp rất nhiều)
Odys

4
Tôi dường như không thể viết ví dụ Deserialize<metadata.DataType>(metadata.Data);. Nó cho tôi biết không thể giải quyết siêu dữ liệu biểu tượng . Làm thế nào để lấy DataType để sử dụng nó cho một phương thức chung?
Cœur
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.