Nie można niejawnie przekonwertować typu „Int” na „T”


90

Mogę zadzwonić Get<int>(Stat);lubGet<string>(Name);

Ale podczas kompilacji otrzymuję:

Nie można niejawnie przekonwertować typu „int” na „T”

i to samo dla 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
Prawdopodobnie myślisz, że blok if sprawdził, czy T jest int, więc wewnątrz bloku wiesz, że T jest int i powinieneś być w stanie niejawnie przekonwertować int na T. Ale kompilator nie jest zaprojektowany do podążania za tym rozumowaniem, po prostu wie że generalnie T nie pochodzi od int, więc nie zezwala na niejawną konwersję. (A jeśli kompilator to obsługiwał, weryfikator nie byłby
— możliwy

Odpowiedzi:


132

Za każdym razem, gdy włączasz typ w rodzaju ogólnym , prawie na pewno robisz coś złego . Generyczne powinny być generyczne ; powinny działać identycznie, całkowicie niezależnie od typu .

Jeśli T może mieć tylko wartość int lub string, nie pisz swojego kodu w ten sposób w ogóle. Napisz dwie metody, jedną zwracającą int, a drugą zwracającą ciąg.


1
Uzyskaj <Samochód>, gdzie samochód implementuje IConvertible, który spowoduje uszkodzenie. Kiedy ktoś zobaczy, że masz ogólną metodę, założy, że może przekazać wszystko, co implementuje IConvertible.
— Tjaart

10
Mogę tylko częściowo się z Tobą zgodzić, @Eric. Mam sytuację, w której muszę parsować tablice zapisane w tagach XML, Problem w tym, że specyfikacja, za którą odpowiada dokument XML (w moim przypadku COLLADA) mówi, że takie tablice mogą być nie tylko float, int i bool, ale także niektóre niestandardowe typy. Jednak w przypadku, gdy otrzymasz float [] (tagi tablicowe zawierają typ przechowywanych danych w ich nazwach: float_array przechowuje zmiennoprzecinkowe), musisz przeanalizować ciąg jako tablicę floats, co wymaga użycia jakiegoś IFormatProvider). Oczywiście nie mogę użyć "T.Parse (...)". Więc dla małego podzbioru przypadków potrzebuję takiego przełączania.
— rbaleksandar

1
Ta odpowiedź utrzyma cię z dala od króliczej nory. Chciałem stworzyć funkcję ogólną dla int, int?, bool, bool?, stringi wydawało się, że jest to niemożliwe.
— Jess

To sprawia, że ​​przełączenie na ogólny typ wyliczeniowy jest praktyczne.
— David A. Grey

1
Nie chciałem tego używać jako odpowiedzi. Ale on ma rację. Chciałem sprawdzić typ i, jeśli jest konkretny, ustawić na nim właściwość. Rozwiązaniem było stworzenie metody wymagającej silnie wpisanego parametru.
— Matt Dawdy,

141

Powinieneś móc po prostu użyć Convert.ChangeType()zamiast niestandardowego kodu:

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

21
Co powiesz nareturn (T)(object)PlayerStats[type];
— maxp

11
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;ponieważ nie są konieczne żadne sprawdzenia zerowe.
— BoltClock

To powyżej nie skompiluje się dla mnie. T musi być typem referencyjnym dla „as” do kompilacji.
— Robert Schmidt

9

ChangeTypeto prawdopodobnie najlepsza opcja. Moje rozwiązanie jest podobne do tego dostarczonego przez BrokenGlass z odrobiną logiki try catch.

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

Mój przypadek użycia to konkretna klasa wywodząca się z ogólnej klasy abstrakcyjnej. Klasa jest oznaczona jako abstrakcyjna, ponieważ definiuje metodę abstrakcyjną, która działa na generycznym prywatnym elemencie członkowskim klasy bazowej. Rodzaj ogólny używa ograniczenia wyliczeniowego C # 7,3 dla jego typu ogólnego. Właśnie pomyślnie ukończyłem test i działa dokładnie tak, jak się spodziewałem.
— David A. Gray,

8

Spróbuj tego:

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

Dziękuję, że pomogło, moja potrzeba jest inna. Piszę metodę próbną dla istniejącej metody statycznej, aby móc ją przetestować. Używając tego osherove.com/blog/2012/7/8/…
— Esen

8

Właściwie możesz po prostu przekonwertować go na, objecta następnie na T.

T var = (T)(object)42;

Przykład dla 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
    }
}

Czasami takie zachowanie jest pożądane. Na przykład podczas implementowania lub zastępowania metody ogólnej z klasy bazowej lub interfejsu i chcesz dodać różne funkcje w zależności od Ttypu.


6

Biorąc pod uwagę, że logika @BrokenGlass ( Convert.ChangeType) nie obsługuje typu GUID.

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

Błąd : nieprawidłowe przesyłanie z „System.String” na „System.Guid”.

Zamiast tego użyj poniższej logiki TypeDescriptor.GetConverter, dodając System.ComponentModelprzestrzeń nazw.

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

Przeczytaj to .



0

Możesz po prostu rzucić jak poniżej,

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;
 }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.