10 Stimmen

Wie überprüft man, ob ein (generischer) Zahlentyp ein ganzzahliger oder nicht ganzzahliger Typ in C# ist?

Ich habe einen generischen Typ T . Verwendung von Marcs Operator Klasse Ich kann damit Berechnungen durchführen.

Ist es möglich, durch bloße Berechnungen festzustellen, ob der Typ ein Integral oder eine nichtintegral Typ?

Vielleicht gibt es eine bessere Lösung? Ich würde es vorziehen, jeden möglichen Typ zu unterstützen, also würde ich gerne verhindern, dass fest kodiert wird, welche Typen integral/nicht-integral sind.

Hintergrundinformationen

Die Situation, in der ich mich befinde, ist, dass ich eine double a T sondern runden auf den nächsten Wert von T zum double Wert.

int a = (int)2.6 führt zu 2 während ich möchte, dass es zu einer 3 , ohne den Typ zu kennen (in diesem Fall int ). Es könnte auch sein double in diesem Fall möchte ich, dass das Ergebnis lautet 2.6 .

6voto

Chris Punkte 26614

Haben Sie versucht Convert.ChangeType ? Etwa so:

Convert.ChangeType(1.9d, typeof (T))

Es wird für alle numerischen Typen funktionieren, denke ich (solange der erste Parameter iConvertible ist und der Typ ein unterstützter Typ ist, der alle grundlegenden numerischen Typen sein sollten, glaube ich).

Es ist wichtig zu erwähnen, dass dies etwas wie double.ToInt32 aufruft, das die Werte rundet, anstatt sie abzuschneiden (Rundung durch die Bank, glaube ich).

Ich habe dies in einem kleinen LinqPad-Programm getestet und es tut, was ich denke, dass Sie wollen:

void Main()
{
    var foo = RetNum<decimal>();
    foo.Dump();
}

public static T RetNum<T>()
{
    return (T)Convert.ChangeType(1.9d, typeof (T));
}

3voto

Hier ist eine Methode, die feststellt, ob ein bestimmter Wert, der in einem generischen numerischen Typ gespeichert ist, eine ganze Zahl ist, ohne hart zu kodieren. Funktioniert bei mir unter .NET 4. Behandelt korrekt alle eingebauten numerischen Typen (wie im MSDN-Link unten definiert) außer BigInteger die nicht implementiert IConvertible .

        public static bool? IsInteger<T>(T testNumber) where T : IConvertible
        {
            // returns null if T is non-numeric
            bool? isInt = null;
            try
            {
                isInt = testNumber.ToUInt64(CultureInfo.InvariantCulture) == testNumber.ToDouble(CultureInfo.InvariantCulture);
            }
            catch (OverflowException)
            {
                // casting a negative int will cause this exception
                try
                {
                    isInt = testNumber.ToInt64(CultureInfo.InvariantCulture) == testNumber.ToDouble(CultureInfo.InvariantCulture);
                }
                catch
                {
                    // throw depending on desired behavior
                }
            }
            catch
            {
                // throw depending on desired behavior
            }
            return isInt;
        }

Hier ist eine Methode, mit der festgestellt werden kann, ob ein bestimmter Typ ein Integraltyp ist.

    public static bool? IsIntegerType<T>() where T : IConvertible
    {
        bool? isInt = null;
        try
        {
            isInt = Math.Round((double)Convert.ChangeType((T)Convert.ChangeType(0.1d, typeof(T)),typeof(double)), 1) != .1d;
            // if you don't round it and T is float you'll get the wrong result
        }
        catch
        {   
            // T is a non numeric type, or something went wrong with the activator
        }
        return isInt;
    }

Convert.ChangeType ist der Weg zur Konvertierung zwischen zwei generischen numerischen Typen mit Rundung. Aber zum Spaß und aus Neugierde, hier ist ein Weg, um einen generischen numerischen Typ in einen int die ohne allzu große Schwierigkeiten erweitert werden könnte, um einen generischen Typ zurückzugeben.

    public static int GetInt32<T>(T target) where T : IConvertible
    {
        bool? isInt = IsInteger<T>(target);
        if (isInt == null) throw new ArgumentException(); // put an appropriate message in
        else if (isInt == true)
        {
            try
            {
                int i = target.ToInt32(CultureInfo.InvariantCulture);
                return i;
            }
            catch
            {   // exceeded size of int32
                throw new OverflowException(); // put an appropriate message in
            }
        }
        else
        {
            try
            {
                double d = target.ToDouble(CultureInfo.InvariantCulture);
                return (int)Math.Round(d);
            }
            catch
            {   // exceeded size of int32
                throw new OverflowException(); // put an appropriate message in
            }
        }
    }

Meine Ergebnisse:

        double d = 1.9;
        byte b = 1;
        sbyte sb = 1;
        float f = 2.0f;
        short s = 1;
        int i = -3;
        UInt16 ui = 44;
        ulong ul = ulong.MaxValue;
        bool? dd = IsInteger<double>(d); // false
        bool? dt = IsInteger<DateTime>(DateTime.Now); // null
        bool? db = IsInteger<byte>(b); // true
        bool? dsb = IsInteger<sbyte>(sb); // true
        bool? df = IsInteger<float>(f); // true
        bool? ds = IsInteger<short>(s); // true
        bool? di = IsInteger<int>(i); // true
        bool? dui = IsInteger<UInt16>(ui); // true
        bool? dul = IsInteger<ulong>(ul); // true
        int converted = GetInt32<double>(d); // coverted==2
        bool? isd = IsIntegerType<double>(); // false
        bool? isi = IsIntegerType<int>(); // true

Zusätzlich, diese MSDN-Seite hat einige Beispielcodes, die hilfreich sein könnten. Er enthält insbesondere eine Liste von Typen, die als numerisch gelten.

2voto

David Yaw Punkte 26593

Ich bin mir nicht 100% sicher, was Sie fragen, aber:

Um zu prüfen, ob es sich um ein Integral handelt Typ verwenden Sie dies: if (obj is float || obj is double) , oder if typeof(T) == typeof(float) || typeof(T) == typeof(double))

Um zu prüfen, ob es sich um ein Integral handelt Wert in einen Double umwandeln, und dann if(value == Math.Round(value))

Das setzt natürlich voraus, dass Sie überhaupt eine Nummer haben. Ich glaube, dass die Operator-Klasse, die Sie verwenden, Dinge wie DateTime unterstützt. Wäre es besser, Ihre generische Methode mit einer generischen Einschränkung zu versehen where T : IConvertible ? Auf diese Weise gäbe es explizite ToDouble y ToInteger Methoden.

Modifier :

Ich glaube, ich verstehe: Sie haben zwei lokale Variablen, double d; T num; . Sie möchten eine Rolle spielen d zu tippen T aber mit korrekter Rundung, wenn T ist ein integraler Typ. Ist das richtig?

Wenn das stimmt, würde ich wie folgt vorgehen:

public void SomeMethod<T>()
{
    double d;
    // I think I got all the floating-point types. There's only a few, so we can test for them explicitly.
    if(typeof(T) != typeof(double) && typeof(T) != typeof(float) && typeof(T) != typeof(Decimal))
    {
        d = Math.Round(d);
    }
    T converted = Convert.ChangeType(d, typeof(T));
}

2voto

Steven Jeuris Punkte 17118

Chris' Antwort bietet eine mögliche Lösung für das von mir erwähnte Szenario, aber aus Leistungsgründen versuche ich immer noch, die eigentliche Frage zu beantworten.

Die Annahme (ungetestet) ist, Convert.ChangeType ist viel langsamer als Math.Round() . Im Idealfall kann ich einmal prüfen, ob der angegebene Typ ganzzahlig ist oder nicht, und dann bedingt aufrufen Math.Round() von da an eine viel effizientere Lösung als der Aufruf von Convert.ChangeType() ständig.

Ich versuche, die folgende Implementierung durchzuführen:

  1. Konvertieren Sie beide 3 , 2 y 1 auf den gewünschten unbekannten Typ. (Dies setzt eine Konvertierung von einem int zum numerischen Typ möglich ist, was ohnehin immer möglich sein sollte).
  2. Für den Fall 3 / 2 == 1 ist es ein integraler Typ. Andernfalls handelt es sich um einen nicht-integralen Typ.

Diese Lösung beruht nicht auf der Kenntnis des Typs und verwendet ausschließlich Umrechnungen und Berechnungen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X