C # - Beberapa tipe generik dalam satu daftar


153

Ini mungkin tidak mungkin, tetapi saya memiliki kelas ini:

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

Masih ada lagi, tapi mari kita tetap sederhana. Tipe generik (DataType) terbatas pada tipe nilai dengan pernyataan where. Yang ingin saya lakukan adalah memiliki daftar objek Metadata ini dari berbagai jenis (Tipe Data). Seperti:

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

Apakah ini mungkin?


24
Saya ingin tahu apakah ada manfaat nyata dari pendekatan di bawah ini dibandingkan dengan hanya menggunakan List<object>? Mereka tidak akan berhenti boxing / unboxing, mereka tidak akan menghilangkan kebutuhan untuk casting, dan pada akhirnya, Anda mendapatkan Metadataobjek yang tidak memberi tahu Anda apa-apa tentang yang sebenarnya DataType, saya sedang mencari solusi untuk mengatasi masalah tersebut. Jika Anda akan mendeklarasikan antarmuka / kelas, hanya demi bisa memasukkan tipe generik implement / turunan dalam daftar generik, seberapa jauh perbedaannya daripada menggunakan yang List<object>lain selain memiliki lapisan yang tidak berarti?
Saeb Amini

9
Baik kelas dasar abstrak dan antarmuka menyediakan tingkat kontrol dengan membatasi jenis elemen yang dapat ditambahkan ke daftar. Saya juga tidak bisa melihat bagaimana tinju masuk ke ini.
0b101010

3
Tentu saja, jika Anda menggunakan .NET v4.0 atau lebih tinggi maka kovarians adalah solusinya. List<Metadata<object>>lakukan triknya.
0b101010

2
@ 0b101010 Saya memikirkan hal yang sama, tapi sayangnya varians tidak diperbolehkan pada tipe nilai. Karena OP memiliki structkendala, OP tidak berfungsi di sini. Lihat
nawfal

@ 0b101010, Keduanya hanya membatasi tipe referensi, semua tipe nilai bawaan dan struct apa pun masih dapat ditambahkan. Selain itu, pada akhirnya, Anda memiliki daftar MetaDatajenis referensi alih-alih jenis nilai asli Anda tanpa informasi (waktu kompilasi) tentang jenis nilai yang mendasari setiap elemen, yang secara efektif "tinju".
Saeb Amini

Jawaban:


195
public abstract class Metadata
{
}

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

5
Wow! Saya benar-benar tidak berpikir itu mungkin! Anda seorang penyelamat, kawan!
Carl

2
+10 untuk ini! Saya tidak tahu mengapa ini mengkompilasi .. Persis apa yang saya butuhkan!
Odys

Saya memiliki masalah yang sama, tetapi kelas generik saya memanjang dari kelas generik lain, jadi saya tidak dapat menggunakan solusi Anda ... ada ide tentang perbaikan untuk situasi ini?
Sheridan

10
Apakah ada manfaat dari pendekatan ini dibandingkan dengan yang sederhana List<object>? silakan lihat komentar saya yang diposting di bawah pertanyaan OP.
Saeb Amini

11
@SaebAmini Daftar <object> tidak menunjukkan niat apa pun kepada pengembang, juga tidak mencegah pengembang menembak dirinya sendiri dengan salah menambahkan objek non-MetaData ke daftar. Dengan menggunakan Daftar <MetaData> dipahami daftar apa yang harus berisi. Kemungkinan besar MetaData akan memiliki beberapa properti / metode publik yang belum ditampilkan dalam contoh di atas. Mengaksesnya melalui objek akan membutuhkan gips yang rumit.
Buzz

92

Mengikuti jawaban leppie, mengapa tidak membuat MetaDataantarmuka:

public interface IMetaData { }

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

Adakah yang bisa memberi tahu saya mengapa pendekatan ini lebih baik?
Lazlo

34
Karena tidak ada fungsi umum yang dibagikan - mengapa membuang kelas dasar pada itu? Antarmuka cukup
flq

2
Karena Anda dapat mengimplementasikan antarmuka dalam struct.
Damian Leszczyński - Vash

2
Warisan kelas menggunakan metode virtual, bagaimanapun, kira-kira 1,4x lebih cepat dari metode antarmuka. Jadi jika Anda berencana mengimplementasikan metode / properti non-generik MetaData (virtual) di MetaData <DataType>, pilih kelas abstrak daripada antarmuka, jika kinerja menjadi perhatian. Kalau tidak, menggunakan antarmuka bisa lebih fleksibel.
TamusJRoyce

30

Saya juga menggunakan versi non-generik, menggunakan newkata kunci:

public interface IMetadata
{
    Type DataType { get; }

    object Data { get; }
}

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

Implementasi antarmuka eksplisit digunakan untuk memungkinkan kedua Dataanggota:

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; }
}

Anda bisa mendapatkan tipe nilai penargetan versi:

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)
    {}
}

Ini dapat diperluas ke segala jenis kendala umum.


4
+1 hanya karena Anda menunjukkan cara menggunakannya ( DataTypedan object Databanyak membantu)
Odys

4
Saya sepertinya tidak bisa menulis misalnya Deserialize<metadata.DataType>(metadata.Data);. Ini memberitahu saya tidak dapat menyelesaikan simbol metadata . Bagaimana cara mengambil DataType untuk menggunakannya untuk metode generik?
Cœur
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.