简体   繁体   中英

How to derive signed type from unsigned type generic parameter, and vice-versa?

I want to make generic metods ToSigned, ToUnsinged.
This is what i have

public static class Number<T> {

// public static bool IsSigned = MinValue.Equals(0) ? false : true;
// public static bool IsUnsigned = MinValue.Equals(0) ? true : false;

public static object ToUnsigned(T input)
{
    if (IsUnsigned)
        return input;

    // T is Signed 
    // How to derive unsigned type from signed type ? 
    // return SignedToUnsigned<T, unsigned T>(input);
    return null;
}


public static object ToSigned(T input)
{
    if (IsSigned)
        return input;

    // T is Unsigned 
    // How to derive signed type from unsigned type ? 
    // return UnsignedToSigned<T, signed T> (input);
    return null;
}


}

I have successfully implemented SignedToUnsigned and UnsignedToSigned, but how do I derive the signed type from an unsigned type, or the unsigned type from a signed type ?

There's no inbuilt mechanism for this; you'd have to hard-code it based on the T :

if(typeof(T) == typeof(int)) return new Number<uint>(
       (uint)((Number<int>)value).Value); // or similar
if(typeof(T) == typeof(long)) return new Number<ulong>(
       (ulong)((Number<long>)value).Value); // or similar

now repeat for all expected types.

Note that this isn't as expensive as it'll look, as the JIT happens per- T for value-type T , and the JIT can remove branches based on constants including typeof(T) .

I wonder, though, whether a better option is extension methods on specific T :

public static Number<uint> ToUnsigned(this Number<int> val)
      => new Number<uint>((uint)val.Value); // etc

There's no way to do this built-in.
Also, note that Equals checks if it's the same object, not the same value, so the result for your (IsSigned, IsUnsigned) is wrong.

You could manually map the types with dictionaries (or use if statements, since there are only 4 integer types), and then call your method with reflection:

But if you use ifs and casts instead, and make due without reflection, it will be significantly faster.

public static class Number<T>
    where T:System.IComparable
{

    public static bool IsSigned = MinValue.CompareTo(default(T)) == 0 ? false : true;

    public static bool IsUnsigned = MinValue.CompareTo(default(T)) == 0 ? true : false;



    private static System.Collections.Generic.Dictionary<System.Type, System.Type> MapSignedUnsigned
            = TypeMapSignedToUnsigned();


    private static System.Collections.Generic.Dictionary<System.Type, System.Type> MapUnsignedSigned
            = TypeMapUnsignedToSigned();


    private static System.Collections.Generic.Dictionary<System.Type, System.Type>
        TypeMapSignedToUnsigned()
    {
        System.Collections.Generic.Dictionary<System.Type, System.Type> dict
            = new System.Collections.Generic.Dictionary<System.Type, System.Type>();

        dict.Add(typeof(System.SByte), typeof(System.Byte));
        dict.Add(typeof(System.Int16), typeof(System.UInt16));
        dict.Add(typeof(System.Int32), typeof(System.UInt32));
        dict.Add(typeof(System.Int64), typeof(System.UInt64));

        return dict;
    }


    private static System.Collections.Generic.Dictionary<System.Type, System.Type>
        TypeMapUnsignedToSigned()
    {
        System.Collections.Generic.Dictionary<System.Type, System.Type> dict
            = new System.Collections.Generic.Dictionary<System.Type, System.Type>();

        dict.Add(typeof(System.Byte), typeof(System.SByte));
        dict.Add(typeof(System.UInt16), typeof(System.Int16));
        dict.Add(typeof(System.UInt32), typeof(System.Int32));
        dict.Add(typeof(System.UInt64), typeof(System.Int64));

        return dict;
    }


    public static T2 ToUnsigned<T2>(T input)
    {
        if (IsUnsigned)
            return (T2) (object) input;

        // T is Signed 
        // t is unsigned type for T 
        System.Type t = MapSignedUnsigned[typeof(T)];

        // TUnsigned SignedToUnsigned<TSigned, TUnsigned>(TSigned longValue)
        // return SignedToUnsigned<T, t> (input);
        System.Reflection.MethodInfo method = typeof(Number<T>).GetMethod("SignedToUnsigned");
        System.Reflection.MethodInfo genericMethod = method.MakeGenericMethod(typeof(T), t);

        return (T2) genericMethod.Invoke(null, new object[] { input });
    }


    public static T2 ToSigned<T2>(T input)
    {
        if (IsSigned)
            return (T2) (object) input;

        // T is Unsigned 
        // t is signed type for T 
        System.Type t = MapUnsignedSigned[typeof(T)];
        // TSigned UnsignedToSigned<TUnsigned, TSigned>(TUnsigned ulongValue)
        // return UnsignedToSigned<T, t> (input);
        System.Reflection.MethodInfo method = typeof(Number<T>).GetMethod("UnsignedToSigned");
        System.Reflection.MethodInfo genericMethod = method.MakeGenericMethod(typeof(T), t);

        return (T2)genericMethod.Invoke(null, new object[] { input });
    }


}

Why don't you just use object instead of generics, eg:

public static class Number
{


    private static object GetConstValue(System.Type t, string propertyName)
    {
        System.Reflection.FieldInfo pi = t.GetField(propertyName, System.Reflection.BindingFlags.Static
            | System.Reflection.BindingFlags.Public
            | System.Reflection.BindingFlags.NonPublic
            );

        return pi.GetValue(null);
    } // End Function GetConstValue 


    private static object GetMinValue(System.Type t)
    {
        return GetConstValue(t, "MinValue");
    } // End Function GetMinValue 


    private static object GetMaxValue(System.Type t)
    {
        return GetConstValue(t, "MaxValue");
    } // End Function GetMaxValue 



    private static object UnsignedToSigned(object value, System.Type t)
    {
        if (object.ReferenceEquals(t, typeof(System.UInt64)))
            return UnsignedToSigned((System.UInt64)value);
        else if (object.ReferenceEquals(t, typeof(System.UInt32)))
            return UnsignedToSigned((System.UInt32)value);
        else if (object.ReferenceEquals(t, typeof(System.UInt16)))
            return UnsignedToSigned((System.UInt16)value);
        else if (object.ReferenceEquals(t, typeof(System.Byte)))
            return UnsignedToSigned((System.Byte)value);

        throw new System.NotImplementedException($"UnsignedToSigned for type {t.Name} is not implemented.");
    }


    public static object UnsignedToSigned(object value)
    {
        if (value == null)
            throw new System.ArgumentNullException(nameof(value), "Parameter cannot be NULL.");

        System.Type t = value.GetType();

        return UnsignedToSigned(value, t);
    }


    public static T UnsignedToSigned<T>(object value)
        where T:System.IComparable 
    {
        if (value == null)
            throw new System.ArgumentNullException(nameof(value), "Parameter cannot be NULL.");

        System.Type t = value.GetType();
        System.Type tRet = typeof(T);

        int sizeRet = System.Runtime.InteropServices.Marshal.SizeOf(tRet);
        int sizeValue = System.Runtime.InteropServices.Marshal.SizeOf(t);

        if (sizeRet != sizeValue)
        {
            throw new System.NotSupportedException($"Type mismatch: {tRet.Name} is not the matching signed type for {t.Name}");
        }

        System.IComparable minValue = (System.IComparable)GetMinValue(t);

        System.IComparable minValueRet = (System.IComparable)GetMinValue(tRet);
        if (minValueRet.CompareTo(System.Convert.ChangeType(0, tRet)) == 0)
        {
            throw new System.NotSupportedException($"Type mismatch: {tRet.Name} is not a signed type.");
        }

        // If we already have an signed type
        // Type mismatch already prevented 
        if (minValue.CompareTo(System.Convert.ChangeType(0, t)) != 0)
        {
            return (T)value;
        }

        return (T)UnsignedToSigned(value, t);
    }

    private static object SignedToUnsigned(object value, System.Type t)
    {
        if (object.ReferenceEquals(t, typeof(System.Int64)))
            return SignedToUnsigned((System.Int64)value);
        else if (object.ReferenceEquals(t, typeof(System.Int32)))
            return SignedToUnsigned((System.Int32)value);
        else if (object.ReferenceEquals(t, typeof(System.Int16)))
            return SignedToUnsigned((System.Int16)value);
        else if (object.ReferenceEquals(t, typeof(System.SByte)))
            return SignedToUnsigned((System.SByte)value);

        throw new System.NotImplementedException("SignedToUnsigned for type " + t.FullName + " is not implemented.");
    }


    public static object SignedToUnsigned(object value)
    {
        if (value == null)
            throw new System.ArgumentNullException(nameof(value), "Parameter cannot be NULL.");

        System.Type t = value.GetType();
        return SignedToUnsigned(value, t);
    }


    public static T SignedToUnsigned<T>(object value)
        where T : System.IComparable
    {
        if (value == null)
            throw new System.ArgumentNullException(nameof(value), "Parameter cannot be NULL.");

        System.Type t = value.GetType();
        System.Type tRet = typeof(T);

        int sizeRet = System.Runtime.InteropServices.Marshal.SizeOf(tRet);
        int sizeValue = System.Runtime.InteropServices.Marshal.SizeOf(t);

        if (sizeRet != sizeValue)
        {
            throw new System.NotSupportedException($"Type mismatch: {tRet.Name} is not the matching unsigned type for {t.Name}");
        }

        System.IComparable minValue = (System.IComparable)GetMinValue(t);

        System.IComparable minValueRet = (System.IComparable)GetMinValue(tRet);
        if (minValueRet.CompareTo(System.Convert.ChangeType(0, tRet)) != 0)
        {
            throw new System.NotSupportedException($"Type mismatch: {tRet.Name} is not an unsigned type.");
        }

        // If we already have an unsigned type
        // Type mismatch already prevented 
        if (minValue.CompareTo(System.Convert.ChangeType(0, t)) == 0)
        {
            return (T)value;
        }

        return (T)SignedToUnsigned(value, t);
    }

    private static System.Int64 UnsignedToSigned(System.UInt64 uintValue)
    {
        return unchecked((System.Int64)uintValue + System.Int64.MinValue);
    }


    private static System.UInt64 SignedToUnsigned(System.Int64 intValue)
    {
        return unchecked((System.UInt64)(intValue - System.Int64.MinValue));
    }


    private static System.Int32 UnsignedToSigned(System.UInt32 uintValue)
    {
        return unchecked((System.Int32)uintValue + System.Int32.MinValue);
    }


    private static System.UInt32 SignedToUnsigned(System.Int32 intValue)
    {
        return unchecked((System.UInt32)(intValue - System.Int32.MinValue));
    }



    private static System.Int16 UnsignedToSigned(System.UInt16 uintValue)
    {
        return (System.Int16) unchecked((System.Int16)uintValue + System.Int16.MinValue);
    }


    private static System.UInt16 SignedToUnsigned(System.Int16 intValue)
    {
        return unchecked((System.UInt16)(intValue - System.Int16.MinValue));
    }



    private static sbyte UnsignedToSigned(byte ulongValue)
    {
        return (sbyte) unchecked((sbyte)ulongValue + sbyte.MinValue);
    }


    private static byte SignedToUnsigned(sbyte longValue)
    {
        return unchecked((byte)(longValue - sbyte.MinValue));
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM