'T' türünün değeri dönüştürülemez


151

Bu muhtemelen acemi bir sorudur, ancak Google şaşırtıcı bir şekilde bir cevap vermedi.

Bu oldukça yapay bir yöntemim var

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

    return t;
}

C ++ arka planından gelince, bunun işe yaramasını bekliyordum. Ancak, yukarıdaki atamaların her ikisi için "'T' türü örtük olarak dizeye dönüştürülemez" ve "'T' türü dizeye dönüştürülemez" ile derleme başarısız olur.

Ya kavramsal olarak yanlış bir şey yapıyorum ya da yanlış sözdizimim var. Lütfen bunu çözmeme yardım edin.

Teşekkür ederim!


20
IMO, jenerik kodunuzdaki türleri kontrol ediyorsanız, muhtemelen jenerikler sorununuz için doğru çözüm değildir.
Austin Salonen

İfade typeof(T) == typeof(string)derleme zamanında değil, çalışma zamanında çözülür. Bu nedenle bloktaki aşağıdaki satır geçersizdir.
Steve Guidi

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

2
@vsapiha, Yalnızca nesne IConvertible uyguladığında çalışır. Olursa tatlılık.
ouflak

Yanıtlar:


293

Bir iç olmasına rağmen ifbloğun, derleyici bunu bilmiyor Tolduğunu string.
Bu nedenle, döküm yapmanıza izin vermez. (Eğer döküm olamayacağını Aynı nedenle DateTimeiçin string)

Sen hiç döküm gerekir object(ki herhangi Tteneke dökme) ve oraya gelen string(çünkü objectolabilir döküm string).
Örneğin:

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

2
Bu çalışıyor! İkinci benzerinin de T newT2 = (T) (nesne) t olması gerektiğini tahmin ediyorum; bu bir işlem olmasa da.
Alex

2
Ekleme: C ++ şablonları, esasen derleme sırasında doğru değerler değiştirilerek kesilir ve yapıştırılır. C # 'da gerçek jenerik şablon (bunun bir "somutlaştırması" değil) derlemeden sonra var olur ve bu nedenle (kelime oyununu bağışlayın) belirtilen tür sınırları boyunca jenerik olmalıdır.

6
Neden sadece "string" kullanarak yayın yapmıyorsunuz? Örneğin, userDefinedValue şu türden olduğunda bu iyi derler (tam anlamıyla hatasız derledim) T:var isBlank = (userDefinedValue is string) && String.IsNullOrWhiteSpace(userDefinedValue as string);
Triynko

2
Bu, derleyici tasarımcıları tarafından bir hata gibi geliyor. Tüm T açıkça nesneye dönüştürülebiliyorsa ve tüm nesneler açık bir şekilde dizeye dönüştürülebiliyorsa, T'nin açıkça dizeye dönüştürülebileceği bir geçiş kuralı olmalıdır. Eğer yanlış bir şekilde çevirme yaparsanız, bir çalışma süresi hatası meydana gelmelidir.
P. Brian.Mackey

1
@SLaks eğer bir hata ise, o zaman çalışma zamanı herhangi bir geçersiz atama gibi bir InvalidCastException oluşturmalıdır. Ama neden nesneye ve sonra T'ye dönüşemediğini anlamıyorum.Triynko'ya katılıyorum, ancak tasarımcıların akıllarında boks / kutudan çıkarma olduğunu varsayıyorum.
Fernando Gómez

11

Her iki hatta aynı sorun var

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

Derleyici T'nin bir dizge olduğunu bilmez ve bu yüzden bunu nasıl atayacağını bilmesinin bir yolu yoktur. Ama kontrol ettiğinden beri zorlayabilirsin

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

zaten bir dizge olduğu için t'yi çevirmenize gerek yoktur, ayrıca kısıtlama eklemeniz gerekir

where T : class

2
Yanlış. Bu derlenmeyecek. Cevabımı gör.
SLaks

2
Gayet iyi derler (nerede olduğu ile birlikte, gönderdikten birkaç saniye sonra bunu kaçırmış olabilir). Oops nm oyuncu kadrosunu değiştirmeyi unuttu
Doggett

3

OP'nin bu soruda jenerik ayrıştırıcılardan gönderdiği benzer kodu biliyorum. Performans açısından bakıldığında, kullanmak gerekir Unsafe.As<TFrom, TResult>(ref TFrom source)bulunabilir ki, System.Runtime.CompilerServices.Unsafe Nuget paketinin. Bu senaryolarda değer türleri için kutudan kaçınır. Ayrıca Unsafe.As, JIT tarafından iki kez yayın yapmaktan (kullanarak (TResult) (object) actualString) daha az makine kodu üretildiğini düşünüyorum , ancak bunu kontrol etmedim.

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 resmi CoreFX deposunda görebileceğiniz gibi, JIT tarafından verimli makine kodu talimatları ile değiştirilecektir:

Unsafe.As Kaynak Kodu


1

Açık türleri kontrol ediyorsanız, bu değişkenleri neden T's olarak tanımlıyorsunuz ?

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
İkinci satır bir tür değişkeni oluşturur T.
SLaks

Türü neden kontrol ettin diye soruyorsun? Depolanan bir temel alan türünüz olduğunu varsayalım.objectDeğerleri türetilmiş türlerle değerleristring . Bu alanların da "DefaultIfNotProvided" değerine sahip olduğunu varsayalım, bu nedenle kullanıcı tarafından sağlanan değerin (bir nesne veya bir dize veya hatta bir sayısal ilkel olabilir) ile eşdeğer olup olmadığını kontrol etmeniz gerekir default(T). Dize, boş / boşluk dizgesinin öntanımlı (T) ile aynı şekilde değerlendirildiği özel bir durum olarak değerlendirilebilir, bu nedenle olup olmadığını kontrol etmek isteyebilirsiniz T userValue; var isBlank = (userValue is string) && String.IsNullOrWhitespace(userValue as string);.
Triynko

0

Hem sınıfınız hem de yönteminiz için genel bir bildiriminiz varsa bu hatayı da alırsınız. Örneğin, aşağıda gösterilen kod bu derleme hatasını verir.

public class Foo <T> {

    T var;

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

}

Bu kod derleme yapar (not T yöntem bildiriminden kaldırılmıştır):

public class Foo <T> {

    T var;

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

}

-5

Bu satırı değiştirin:

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

Bu hat için:

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

1
onlar aynı
bigworld12

Her ikisi de aynıdır ... sadece dil anahtar kelimesini kullanmak yerine sınıf kitaplığı API'lerini kullanmak.
Abdulhameed
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.