Bagaimana cara membuat tipe pengembalian metode generik?

166

Apakah ada cara untuk membuat metode ini generik sehingga saya dapat mengembalikan string, bool, int, atau double? Saat ini, ia mengembalikan sebuah string, tetapi jika ia dapat menemukan "true" atau "false" sebagai nilai konfigurasi, saya ingin mengembalikan bool misalnya.

    public static string ConfigSetting(string settingName)
    {  
         return ConfigurationManager.AppSettings[settingName];
    }
MacGyver
sumber
Apakah ada cara bagi Anda untuk mengetahui tipe pengaturan apa?
thecoshman
2
Saya pikir pertanyaan yang benar-benar ingin Anda tanyakan adalah "Bagaimana cara saya membuat aplikasi saya diketik dengan kuat?" Sudah terlalu lama sejak saya bekerja dengan itu untuk menulis jawaban yang tepat.
Simon
Yah, idealnya saya tidak mau harus memasukkan tipe ke dalam metode. Saya hanya akan memiliki 4 jenis yang saya sebutkan. Jadi jika "benar" / "salah" diset, saya ingin fungsi ini mengembalikan boolean (tanpa perlu memasukkannya ke dalam metode), saya mungkin dapat menggabungkan int dan dobel menjadi hanya ganda, dan yang lainnya harus berupa string. Apa yang dijawab sudah akan bekerja dengan baik, tetapi saya harus melewati jenis setiap kali, yang mungkin baik-baik saja.
MacGyver
3
Komentar Anda terdengar seperti Anda meminta metode yang akan mengembalikan bool yang sangat diketik (atau string, atau int, atau apa pun yang Anda miliki) pada saat runtime berdasarkan pada data aktual yang diambil untuk kunci nama pengaturan. C # tidak akan melakukannya untuk Anda; tidak mungkin Anda bisa mengetahui jenis nilai itu pada waktu kompilasi. Dengan kata lain, itu pengetikan dinamis, bukan pengetikan statis. C # dapat melakukannya untuk Anda jika Anda menggunakan dynamickata kunci. Ada biaya kinerja untuk itu, tetapi untuk membaca file konfigurasi, biaya kinerja hampir pasti tidak signifikan.
phoog

Jawaban:

354

Anda harus menjadikannya metode generik, seperti ini:

public static T ConfigSetting<T>(string settingName)
{  
    return /* code to convert the setting to T... */
}

Tetapi penelepon harus menentukan jenis yang mereka harapkan. Anda kemudian dapat berpotensi menggunakan Convert.ChangeType, dengan asumsi bahwa semua jenis yang relevan didukung:

public static T ConfigSetting<T>(string settingName)
{  
    object value = ConfigurationManager.AppSettings[settingName];
    return (T) Convert.ChangeType(value, typeof(T));
}

Saya tidak sepenuhnya yakin bahwa semua ini adalah ide yang baik, ingatlah ...

Jon Skeet
sumber
21
/ * kode untuk mengubah pengaturan ke T ... * / dan di sini mengikuti seluruh novel :)
Adrian Iftode
1
Apakah ini tidak mengharuskan Anda untuk mengetahui jenis pengaturan yang ingin Anda dapatkan, yang mungkin tidak dapat dilakukan.
thecoshman
2
@ thecoshman: Ya, tetapi jika Anda tidak melakukannya, apa yang Anda lakukan dengan nilai yang dikembalikan?
George Duckett
5
Sementara jawaban ini tentu saja benar, dan seperti yang Anda perhatikan permintaan OP memenuhi, itu mungkin layak disebutkan bahwa pendekatan lama metode yang terpisah ( ConfigSettingString, ConfigSettingBool, dll) memiliki keuntungan dari tubuh metode yang akan lebih pendek, lebih jelas, dan lebih baik fokus .
phoog
4
Jika ini tidak direkomendasikan, lalu apa tujuan dari tipe pengembalian generik?
bobbyalex
29

Anda bisa menggunakan Convert.ChangeType():

public static T ConfigSetting<T>(string settingName)
{
    return (T)Convert.ChangeType(ConfigurationManager.AppSettings[settingName], typeof(T));
}
Gelas pecah
sumber
13

Ada banyak cara untuk melakukan ini (dicantumkan berdasarkan prioritas, khusus untuk masalah OP)

  1. Opsi 1: Pendekatan lurus - Buat beberapa fungsi untuk setiap jenis yang Anda harapkan daripada memiliki satu fungsi generik.

    public static bool ConfigSettingInt(string settingName)
    {  
         return Convert.ToBoolean(ConfigurationManager.AppSettings[settingName]);
    }
  2. Opsi 2: Bila Anda tidak ingin menggunakan metode konversi mewah - Cast nilai ke objek dan kemudian ke tipe generik.

    public static T ConfigSetting<T>(string settingName)
    {  
         return (T)(object)ConfigurationManager.AppSettings[settingName];
    }

    Catatan - Ini akan menimbulkan kesalahan jika gips tidak valid (kasing Anda). Saya tidak akan merekomendasikan melakukan ini jika Anda tidak yakin tentang jenis casting, lebih baik pergi untuk opsi 3.

  3. Opsi 3: Generik dengan keamanan tipe - Buat fungsi generik untuk menangani konversi tipe.

    public static T ConvertValue<T,U>(U value) where U : IConvertible
    {
        return (T)Convert.ChangeType(value, typeof(T));
    } 

    Note - T adalah tipe yang diharapkan, perhatikan batasan mana di sini (tipe U harus IConvertible untuk menyelamatkan kita dari kesalahan)

RollerCosta
sumber
4
Mengapa membuat opsi ketiga menjadi generik U? Tidak ada gunanya melakukannya, dan itu membuat metode ini lebih sulit untuk dipanggil. Terima IConvertiblesaja. Saya tidak berpikir itu layak termasuk opsi kedua untuk pertanyaan ini mengingat tidak menjawab pertanyaan yang diajukan. Anda mungkin juga harus mengganti nama metode pada opsi pertama ...
Jon Skeet
7

Anda harus mengonversi jenis nilai pengembalian metode ke jenis Generik yang Anda berikan ke metode saat menelepon.

    public static T values<T>()
    {
        Random random = new Random();
        int number = random.Next(1, 4);
        return (T)Convert.ChangeType(number, typeof(T));
    }

Anda perlu melewati tipe yang tipe kasta untuk nilai yang Anda kembalikan melalui metode itu.

Jika Anda ingin mengembalikan nilai yang bukan tipe kasta ke tipe generik yang Anda lewati, Anda mungkin harus mengubah kode atau memastikan Anda melewati tipe yang dapat di-kasta untuk nilai pengembalian metode. Jadi, pendekatan ini tidak direkomendasikan.

Vinay Chanumolu
sumber
Temukan - bagi saya baris terakhir return (T)Convert.ChangeType(number, typeof(T));adalah apa yang saya lewatkan - sorakan
Greg Trevellick
1

Buat fungsi dan bagikan parameter put sebagai tipe generik.

 public static T some_function<T>(T out_put_object /*declare as Output object*/)
    {
        return out_put_object;
    }
Prabhakar
sumber
Ini sebenarnya cukup pintar untuk beberapa kasus penggunaan. Seperti menarik data dari database. Anda tahu Anda akan mendapatkan daftar data tipe T. Metode pemuatan tidak tahu jenis T yang Anda inginkan SEKARANG. Jadi, cukup lewati Daftar baru <WantedObject> untuk ini dan metode ini dapat melakukan tugasnya dan mengisi daftar sebelum mengembalikannya. Bagus!
Marco Heumann
0

Silakan coba kode di bawah ini:

public T? GetParsedOrDefaultValue<T>(string valueToParse) where T : struct, IComparable
{
 if(string.EmptyOrNull(valueToParse))return null;
  try
  {
     // return parsed value
     return (T) Convert.ChangeType(valueToParse, typeof(T));
  }
  catch(Exception)
  {
   //default as null value
   return null;
  }
 return null;
}
Sid
sumber
-1
 private static T[] prepareArray<T>(T[] arrayToCopy, T value)
    {
        Array.Copy(arrayToCopy, 1, arrayToCopy, 0, arrayToCopy.Length - 1);
        arrayToCopy[arrayToCopy.Length - 1] = value;
        return (T[])arrayToCopy;
    }

Saya melakukan ini di seluruh kode saya dan ingin cara untuk memasukkannya ke dalam metode. Saya ingin membagikan ini di sini karena saya tidak harus menggunakan Convert.ChangeType untuk nilai pengembalian saya. Ini mungkin bukan praktik terbaik tetapi berhasil bagi saya. Metode ini menggunakan larik tipe generik dan nilai untuk ditambahkan ke akhir larik. Array kemudian disalin dengan nilai pertama yang dilucuti dan nilai yang diambil ke dalam metode ditambahkan ke akhir array. Hal terakhir adalah saya mengembalikan array generik.

Adam Howard
sumber