Nilai tipe 'T' tidak dapat dikonversi ke


146

Ini mungkin pertanyaan pemula, tetapi secara mengejutkan google tidak memberikan jawaban.

Saya memiliki metode yang agak buatan ini

T HowToCast<T>(T t)
{
    if (typeof(T) == typeof(string))
    {
        T newT1 = "some text";
        T newT2 = (string)t;
    }

    return t;
}

Berasal dari latar belakang C ++ saya berharap ini berfungsi. Namun, gagal mengkompilasi dengan "Tidak dapat secara implisit mengonversi tipe 'T' ke string" dan "Tidak dapat mengonversi tipe 'T' ke string" untuk kedua tugas di atas.

Saya melakukan sesuatu yang secara konseptual salah atau hanya memiliki sintaks yang salah. Tolong bantu saya menyelesaikan masalah ini.

Terima kasih!


20
IMO, jika Anda memeriksa jenis dalam kode obat generik, maka obat generik mungkin bukan solusi yang tepat untuk masalah Anda.
Austin Salonen

Ekspresi typeof(T) == typeof(string)diselesaikan pada saat runtime, bukan waktu kompilasi. Dengan demikian, baris berikut dalam blok tidak valid.
Steve Guidi

8
(T) Convert.ChangeType (newT1, typeof (T))
vsapiha

2
@vsapiha, Hanya berfungsi jika objek mengimplementasikan IConvertible. Manisnya jika itu terjadi.
ouflak

Jawaban:


285

Meskipun itu dalam sebuah ifblok, compiler tidak tahu bahwa Tadalah string.
Karena itu, ia tidak membiarkan Anda bermain. (Untuk alasan yang sama yang Anda tidak bisa melemparkan DateTimeke string)

Anda perlu dilemparkan ke object, (yang Tdapat dilemparkan ke mana pun ), dan dari sana ke string(karena objectdapat dilemparkan ke string).
Sebagai contoh:

T newT1 = (T)(object)"some text";
string newT2 = (string)(object)t;

2
Ini bekerja! Saya menduga seperti kedua juga harus T newT2 = (T) (objek) t; meskipun itu bukan op.
Alex

2
Tambahan: C ++ templat pada dasarnya cut-and-paste pada waktu kompilasi dengan nilai yang benar diganti. Dalam C # templat generik aktual (bukan "instantiasi" itu) ada setelah kompilasi dan karenanya (maaf pun) harus generik melintasi batas tipe yang ditentukan.

(string) (objek) t; tidak melakukan apa pun di sini, mungkin juga meninggalkan itu, (string) (objek) yaitu
Doggett

6
Mengapa tidak hanya menggunakan "sebagai string"? Sebagai contoh, ini mengkompilasi dengan baik (saya benar-benar hanya mengkompilasinya tanpa kesalahan) ketika userDefinedValue adalah tipe T:var isBlank = (userDefinedValue is string) && String.IsNullOrWhiteSpace(userDefinedValue as string);
Triynko

1
Ini terasa seperti kesalahan oleh desainer kompiler. Jika semua T dapat secara eksplisit dilemparkan ke objek dan semua objek dapat secara eksplisit dilemparkan ke string maka harus ada aturan transitif yang T dapat secara eksplisit dilemparkan ke string. Jika Anda salah melakukan gips maka kesalahan runime akan terjadi.
P.Brian.Mackey

10

Kedua jalur memiliki masalah yang sama

T newT1 = "some text";
T newT2 = (string)t;

Kompiler tidak tahu bahwa T adalah sebuah string dan karenanya tidak memiliki cara untuk mengetahui cara menetapkan itu. Tetapi karena Anda memeriksa Anda bisa memaksanya

T newT1 = "some text" as T;
T newT2 = t; 

Anda tidak perlu membuang t karena ini sudah berupa string, juga perlu menambahkan batasan

where T : class

2
Salah. Ini tidak akan dikompilasi. Lihat jawaban saya.
SLaks

2
Mengkompilasi dengan baik (dengan di mana itu, menambahkan bahwa beberapa detik setelah saya diposting, mungkin telah melewatkan itu). Ups nm lupa ganti gips
Doggett

2

Saya tahu kode serupa yang OP posting dalam pertanyaan ini dari parser generik. Dari perspektif kinerja, Anda harus menggunakan Unsafe.As<TFrom, TResult>(ref TFrom source), yang dapat ditemukan dalam paket System.Runtime.CompilerServices.Unsafe NuGet. Ini menghindari tinju untuk tipe nilai dalam skenario ini. Saya juga berpikir bahwa Unsafe.Asmenghasilkan lebih sedikit kode mesin yang dihasilkan oleh JIT daripada melakukan casting dua kali (menggunakan (TResult) (object) actualString), tapi saya belum memeriksanya.

public TResult ParseSomething<TResult>(ParseContext context)
{
    if (typeof(TResult) == typeof(string))
    {
        var token = context.ParseNextToken();
        string parsedString = token.ParseToDotnetString();
        return Unsafe.As<string, TResult>(ref parsedString);
    }
    else if (typeof(TResult) == typeof(int))
    {
        var token = context.ParseNextToken();
        int parsedInt32 = token.ParseToDotnetInt32();
        // This will not box which might be critical to performance
        return Unsafe.As<int, TResult>(ref parsedInt32); 
    }
    // other cases omitted for brevity's sake
}

Unsafe.As akan digantikan oleh JIT dengan instruksi kode mesin yang efisien, seperti yang dapat Anda lihat di repo CoreFX resmi:

Kode Sumber Unsafe.As


1

Jika Anda memeriksa tipe eksplisit, mengapa Anda mendeklarasikan variabel tersebut sebagai variabel T?

T HowToCast<T>(T t)
{
    if (typeof(T) == typeof(string))
    {
        var newT1 = "some text";
        var newT2 = t;  //this builds but I'm not sure what it does under the hood.
        var newT3 = t.ToString();  //for sure the string you want.
    }

    return t;
}

6
Baris kedua menciptakan variabel tipe T.
SLaks

Anda bertanya mengapa memeriksa tipenya? Misalkan Anda memiliki tipe bidang dasar yang menyimpan objectnilai, dengan tipe turunan yang menyimpan stringnilai. Misalkan bidang ini juga memiliki nilai "DefaultIfNotProvided", jadi Anda perlu memeriksa apakah nilai yang disediakan pengguna (yang bisa berupa objek atau string atau bahkan primitif numerik) setara dengan default(T). String dapat diperlakukan sebagai kasus khusus di mana string kosong / spasi diperlakukan sama dengan default (T), jadi Anda mungkin ingin memeriksa apakah T userValue; var isBlank = (userValue is string) && String.IsNullOrWhitespace(userValue as string);.
Triynko

0

Anda juga akan mendapatkan kesalahan ini jika Anda memiliki deklarasi generik untuk kelas Anda dan metode Anda. Misalnya kode yang ditunjukkan di bawah ini memberikan kesalahan kompilasi ini.

public class Foo <T> {

    T var;

    public <T> void doSomething(Class <T> cls) throws InstantiationException, IllegalAccessException {
        this.var = cls.newInstance();
    }

}

Kode ini dikompilasi (catatan T dihapus dari deklarasi metode):

public class Foo <T> {

    T var;

    public void doSomething(Class <T> cls) throws InstantiationException, IllegalAccessException {
        this.var = cls.newInstance();
    }

}

-5

Ubah baris ini:

if (typeof(T) == typeof(string))

Untuk baris ini:

if (t.GetType() == typeof(string))

1
mereka sama
bigworld12

Keduanya sama ... hanya menggunakan kata kunci bahasa vs menggunakan API perpustakaan kelas.
Abdulhameed
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.