Typ 'Int' kann nicht implizit in 'T' konvertiert werden.

89

Ich kann anrufen Get<int>(Stat);oderGet<string>(Name);

Aber beim Kompilieren bekomme ich:

Typ 'int' kann nicht implizit in 'T' konvertiert werden.

und das gleiche für 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;
    }
}
David W.
quelle
6
Sie denken wahrscheinlich, dass der if-Block überprüft hat, ob T int ist. Innerhalb des Blocks wissen Sie also, dass T int ist, und Sie sollten in der Lage sein, int implizit in T zu konvertieren. Der Compiler ist jedoch nicht darauf ausgelegt, dieser Argumentation zu folgen, er weiß es nur dass T im Allgemeinen nicht von int abgeleitet ist, so dass es die implizite Konvertierung nicht zulässt. (Und wenn der Compiler dies unterstützen würde, würde der Verifizierer dies nicht tun, sodass die kompilierte Assembly nicht
überprüfbar

Antworten:

130

Jedes Mal, wenn Sie einen Typ in einem Generikum einschalten, machen Sie mit ziemlicher Sicherheit etwas falsch . Generika sollten generisch sein ; Sie sollten unabhängig vom Typ identisch arbeiten .

Wenn T nur int oder string sein kann, schreiben Sie Ihren Code überhaupt nicht so. Schreiben Sie zwei Methoden, eine, die ein int und eine, die eine Zeichenfolge zurückgibt.

Eric Lippert
quelle
1
Holen Sie sich <Car>, wo Auto implementiert IConvertible wird Bruch verursachen. Wenn jemand sieht, dass Sie eine generische Methode haben, geht er davon aus, dass er alles übergeben kann, was IConvertible implementiert.
Tjaart
10
Ich kann Ihnen nur teilweise zustimmen, @Eric. Ich habe eine Situation, in der ich in XML-Tags gespeicherte Arrays analysieren muss. Das Problem ist, dass die Spezifikation, der das XML-Dokument folgt (in meinem Fall COLLADA), besagt, dass solche Arrays sein können nicht nur float, int und bool, sondern auch einige benutzerdefinierte Typen. Wenn Sie jedoch ein float [] erhalten (Array-Tags enthalten den Typ der gespeicherten Daten in ihren Namen: float_array speichert float), müssen Sie die Zeichenfolge als Array von analysieren Floats, für die ein IFormatProvider verwendet werden muss). Ich kann "T.Parse (...)" offensichtlich nicht verwenden. Für eine kleine Teilmenge von Fällen muss ich eine solche Umschaltung verwenden.
Rbaleksandar
1
Diese Antwort hält Sie aus dem Kaninchenbau heraus. Ich wollte eine generische Funktion für erstellen int, int?, bool, bool?, string, und es schien unmöglich.
Jess
Dies macht das Einschalten eines generischen Aufzählungstyps praktisch.
David A. Gray
1
Ich wollte das nicht als Antwort verwenden. Aber er hat recht. Ich wollte den Typ überprüfen und, falls vorhanden, eine Eigenschaft festlegen. Die Lösung bestand darin, eine Methode zu erstellen, die einen stark typisierten Parameter verwendete.
Matt Dawdy
136

Sie sollten in der Lage sein, Convert.ChangeType()anstelle Ihres benutzerdefinierten Codes Folgendes zu verwenden:

public T Get<T>(Stats type) where T : IConvertible
{
    return (T) Convert.ChangeType(PlayerStats[type], typeof(T));
}
Glassplitter
quelle
19
Wie wäre esreturn (T)(object)PlayerStats[type];
maxp
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;
    }
}
Reza ArabQaeni
quelle
2
return (T) t;weil keine Nullprüfungen notwendig sind.
BoltClock
Dies oben wird für mich nicht kompiliert. T muss ein Referenztyp sein, damit "as" kompiliert werden kann.
Robert Schmidt
9

ChangeTypeist wahrscheinlich Ihre beste Option. Meine Lösung ähnelt der von BrokenGlass mit ein wenig Try-Catch-Logik.

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;
}
Michael Ciba
quelle
Mein Anwendungsfall ist eine konkrete Klasse, die von einer generischen abstrakten Klasse abgeleitet ist. Die Klasse ist als abstrakt markiert, da sie eine abstrakte Methode definiert, die für das generische private Mitglied der Basisklasse ausgeführt wird. Das Generikum verwendet die C # 7.3-Enum-Einschränkung für seinen generischen Typ. Ich habe gerade einen Test erfolgreich abgeschlossen und er funktioniert genau so, wie ich es mir erhofft hatte.
David A. Gray
8

Versuche dies:

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];
    }
}
Michael Kalinovich
quelle
Danke das hat geholfen, mein Bedürfnis ist anders. Ich schreibe eine Scheinmethode für eine vorhandene statische Methode, damit ich sie testen kann. Verwenden dieser osherove.com/blog/2012/7/8/…
Esen
8

Eigentlich können Sie es einfach in objectund dann in konvertieren T.

T var = (T)(object)42;

Ein Beispiel für 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
    }
}

Manchmal ist dieses Verhalten wünschenswert. Wenn Sie beispielsweise eine generische Methode aus einer Basisklasse oder Schnittstelle implementieren oder überschreiben und je nach TTyp verschiedene Funktionen hinzufügen möchten .

GregorMohorko
quelle
5

Unter Berücksichtigung von @BrokenGlass wird die Logik ( Convert.ChangeType) den GUID-Typ nicht unterstützt.

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

Error : Ungültige Umwandlung von 'System.String' in 'System.Guid'.

Verwenden Sie stattdessen die folgende Logik, TypeDescriptor.GetConverterindem Sie einen System.ComponentModelNamespace hinzufügen .

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

Lesen Sie dies .

Prasad Kanaparthi
quelle
3

Es sieht so aus, als ob Sie einen brauchen TypeConverter, siehe diesen Blogeintrag .

Ian Mercer
quelle
0

Sie können einfach wie unten besetzen,

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;
 }
}
Vijayanath Viswanathan
quelle