'Int' türü örtük olarak 'T' türüne dönüştürülemez


92

Arayabilirim Get<int>(Stat);veyaGet<string>(Name);

Ama derlerken şunu elde ederim:

'İnt' türü örtük olarak 'T'ye dönüştürülemez

ve aynı şey için string.

public T Get<T>(Stats type) where T : IConvertible
{
    if (typeof(T) == typeof(int))
    {
        int t = Convert.ToInt16(PlayerStats[type]);
        return t;
    }
    if (typeof(T) == typeof(string))
    {
        string t = PlayerStats[type].ToString();
        return t;
    }
}

6
Muhtemelen if bloğunun T'nin int olduğunu kontrol ettiğini düşünüyorsunuz, bu nedenle blok içinde T'nin int olduğunu biliyorsunuz ve int'i örtük olarak T'ye dönüştürebilmelisiniz. Ancak derleyici bu mantığı takip etmek için tasarlanmamıştır, sadece bilir genellikle T'nin int'ten türetilmediği, dolayısıyla örtük dönüştürmeye izin vermediği. (Derleyici destekliyorsa, doğrulayıcı desteklemeyecektir, bu nedenle derlenen derleme doğrulanamaz olacaktır.)
JGWeissman

Yanıtlar:


132

Kendinizi bir jenerikte bir türü değiştirirken bulduğunuzda , neredeyse kesinlikle yanlış bir şeyler yapıyorsunuzdur . Jenerikler genel olmalıdır ; türden tamamen bağımsız olarak aynı şekilde çalışmalıdırlar .

T yalnızca int veya string olabilirse, o zaman kodunuzu ilk etapta bu şekilde yazmayın. Biri int döndüren ve diğeri bir dize döndüren iki yöntem yazın.


1
IConvertible'ın kırılmaya neden olacağı aracın uygulandığı <Car> aracını alın. Birisi sizin genel bir yönteme sahip olduğunuzu gördüğünde, IConvertible'ı uygulayan herhangi bir şeyi geçebileceğini varsayacaktır.
Tjaart

11
Seninle sadece kısmen aynı fikirdeyim @Eric. XML etiketlerinde depolanan dizileri ayrıştırmam gereken bir durum var. Sorun şu ki, XML belgesinin takip ettiği belirtim (benim durumumda COLLADA) bu tür dizilerin sadece float, int ve bool değil, aynı zamanda bazı özel türler de olsa, bir float [] almanız durumunda (dizi etiketleri, adlarında depolanan verilerin türünü içerir: float_array floats depolar) dizeyi bir dizi olarak ayrıştırmanız gerekir. bazı IFormatProvider'ın kullanılmasını gerektiren floats). Açıkçası "T.Parse (...)" kullanamıyorum. Bu nedenle, küçük bir durum alt kümesi için böyle bir anahtarlama kullanmam gerekiyor.
rbaleksandar

1
Bu cevap sizi tavşan deliğinden uzak tutacaktır. İçin genel bir işlev yapmak istedim int, int?, bool, bool?, stringve bu imkansız görünüyordu.
Jess

Bu, genel bir numaralandırılmış türdeki geçişi pratik hale getirir.
David A. Gray

1
Bunu cevap olarak kullanmak istemedim. Ama haklı. Türünü kontrol etmek ve belirli bir türse üzerinde bir özellik ayarlamak istedim. Çözüm, güçlü yazılmış bir parametre alan bir yöntem yapmaktı.
Matt Dawdy

143

Convert.ChangeType()Özel kodunuz yerine yalnızca kullanabilmeniz gerekir :

public T Get<T>(Stats type) where T : IConvertible
{
    return (T) Convert.ChangeType(PlayerStats[type], typeof(T));
}

22
Peki yareturn (T)(object)PlayerStats[type];
maxp

@BrokenGlass Teşekkürler İyi çalışıyor.
ASLIM

@maxp becareful Genel amaç için çalışmaz, örneğin yönetilmeyen türün yayınlanması 'Convert.ChangeType ()' gerektirir.
ASLIM

10
public T Get<T>(Stats type ) where T : IConvertible
{
    if (typeof(T) == typeof(int))
    {
        int t = Convert.ToInt16(PlayerStats[type]);
        return (T)t;
    }
    if (typeof(T) == typeof(string))
    {
        string t = PlayerStats[type].ToString();
        return (T)t;
    }
}

2
return (T) t;çünkü boş kontroller gerekli değildir.
BoltClock

Yukarıdaki bu benim için derlenmeyecek. T'nin "as" ın derlenebilmesi için bir başvuru türü olması gerekir.
Robert Schmidt

9

ChangeTypemuhtemelen en iyi seçeneğinizdir. Benim çözümüm, BrokenGlass tarafından sağlanan ve biraz deneme yakalama mantığına benzer.

static void Main(string[] args)
{
    object number = "1";
    bool hasConverted;
    var convertedValue = DoConvert<int>(number, out hasConverted);

    Console.WriteLine(hasConverted);
    Console.WriteLine(convertedValue);
}

public static TConvertType DoConvert<TConvertType>(object convertValue, out bool hasConverted)
{
    hasConverted = false;
    var converted = default(TConvertType);
    try
    {
        converted = (TConvertType) 
            Convert.ChangeType(convertValue, typeof(TConvertType));
        hasConverted = true;
    }
    catch (InvalidCastException)
    {
    }
    catch (ArgumentNullException)
    {
    }
    catch (FormatException)
    {
    }
    catch (OverflowException)
    {
    }

    return converted;
}

Kullanım durumum, genel bir soyut sınıftan türetilmiş somut bir sınıftır. Sınıf soyut olarak işaretlenmiştir çünkü temel sınıfın genel özel üyesi üzerinde çalışan soyut bir yöntemi tanımlar. Jenerik, kendi genel türünde C # 7.3 Enum kısıtlamasını kullanır. Bir testi başarıyla tamamladım ve tam olarak olmasını umduğum gibi çalışıyor.
David A. Gray

8

Bunu dene:

public T Get<T>(Stats type ) where T : IConvertible
{
    if (typeof(T) == typeof(int))
    {
        return (T)(object)Convert.ToInt16(PlayerStats[type]);

    }
    if (typeof(T) == typeof(string))
    {

        return (T)(object)PlayerStats[type];
    }
}

Teşekkür ederim bu yardımcı oldu, ihtiyacım farklı. Mevcut bir statik metot için sahte bir metot yazıyorum, böylece test edebilirim. Bu osherove.com/blog/2012/7/8/… kullanımı
Esen

8

Aslında, bunu sadece 'a objectve sonra' ye dönüştürebilirsiniz T.

T var = (T)(object)42;

Şunlara bir örnek bool:

public class Program
{
    public static T Foo<T>()
    {
        if(typeof(T) == typeof(bool)) {
            return (T)(object)true;
        }

        return default(T);
    }

    public static void Main()
    {
        bool boolValue = Foo<bool>(); // == true
        string stringValue = Foo<string>(); // == null
    }
}

Bazen bu davranış arzu edilir. Örneğin, bir temel sınıf veya arabirimden genel bir yöntemi uygularken veya geçersiz kılarken ve türe bağlı olarak bazı farklı işlevler eklemek istiyorsanız T.


6

@BrokenGlass mantığının ( Convert.ChangeType) GUID türünü desteklemediğini düşünürsek .

public T Get<T>(Stats type) where T : IConvertible
{
    return (T) Convert.ChangeType(PlayerStats[type], typeof(T));
}

Hata : 'System.String'den' System.Guid'e geçersiz çevrim.

Bunun yerine, ad alanı TypeDescriptor.GetConverterekleyerek aşağıdaki mantığı kullanın System.ComponentModel.

public T Get<T>(Stats type) where T : IConvertible
{
    (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFromInvariantString(PlayerStats[type])
}

Bunu okuyun .



0

Aşağıdaki gibi basitçe döküm yapabilirsiniz,

public T Get<T>(Stats type) where T : IConvertible
{
  if (typeof(T) == typeof(int))
  {
    int t = Convert.ToInt16(PlayerStats[type]);
    return t as T;
  }
 if (typeof(T) == typeof(string))
 {
    string t = PlayerStats[type].ToString();
    return t as T;
 }
}
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.