Сравнение значений в штучной упаковке

То, что я пытаюсь достичь здесь, - это сравнение прямых значений примитивных типов в штучной упаковке.

((object)12).Equals((object)12); // Type match will result in a value comparison,
((object)12).Equals((object)12d); // but a type mismatch will not. (false)
object.Equals((object)12,(object)12d); // Same here. (false)

Я понимаю "почему". Я просто не вижу "как".

Типы неизвестны до времени исполнения, где они могут быть любыми примитивными типами из источника данных. Это включает в себя строки, datetimes, bools и т.д. Я пошел по уродливому пути написания метода расширения, который работает с обоими типами, а затем отбрасывает перед выполнением сравнения "==": (Для полноты я включил каждый примитивный тип, плюс те, которые меня интересовали)

public static bool ValueEquals(this object thisObj, object compare)
    {
        if (thisObj is int)
        {
            int obj = (int)thisObj;
            if (compare is int)
                return (obj == (int)compare);
            if (compare is uint)
                return (obj == (uint)compare);
            if (compare is decimal)
                return (obj == (decimal)compare);
            if (compare is float)
                return (obj == (float)compare);
            <... and so on for each primitive type ...>
        }
        if (thisObj is uint)
        {
            uint obj = (uint)thisObj;
            if (compare is int)
                return (obj == (int)compare);
            if (compare is uint)
                return (obj == (uint)compare);
            <... Again for each primitive type ...>
        }
        if (thisObj is decimal)
        {
            decimal obj = (decimal)thisObj;
            if (compare is int)
                return (obj == (int)compare);
            <... Etc, etc ...>

Результирующий метод оказался длиннее на 300 + строк, что было прекрасно (но отвратительно), но теперь мне нужно сделать больше, чем просто "==". Мне нужно > , <, < =, > =,! =.

Есть ли что-нибудь в Reflection, которое я мог бы использовать для сопоставлений типов в штучной упаковке?

Что-нибудь вообще?

Ответ 1

Похоже, вы предполагаете, что тип arg1 - это тот, который вы хотите преобразовать, поэтому я бы использовал такой жанр. Пока arg2 является IConvertible (int, double, все числа, строка и т.д., Все IConvertible), это будет работать:

public static bool ValueEquality<T1, T2>(T1 val1, T2 val2) 
    where T1 : IConvertible 
    where T2 : IConvertible
{
    // convert val2 to type of val1.
    T1 boxed2 = (T1) Convert.ChangeType(val2, typeof (T1));

    // compare now that same type.
    return val1.Equals(boxed2);
}

** UPDATE ** Созданы оба типа общих аргументов, оба могут быть выведены и добавлены дополнительные возможности безопасности компиляции на arg2, чтобы убедиться, что ICON обратимо во время компиляции.

Учитывая эту общую функцию, теперь все возвращаемые значения возвращают true (не нужно указывать аргумент типа, поскольку он выводится из первого аргумента:

        Console.WriteLine(ValueEquality(1, "1"));
        Console.WriteLine(ValueEquality(2, 2.0));
        Console.WriteLine(ValueEquality(3, 3L));

UPDATE

На основе вашего комментария здесь перегрузка, если все, что у вас есть, есть объекты. Оба могут сосуществовать, и он будет называть один более подходящим на основе аргументов:

    public static bool ValueEquality(object val1, object val2)
    {
        if (!(val1 is IConvertible)) throw new ArgumentException("val1 must be IConvertible type");
        if (!(val2 is IConvertible)) throw new ArgumentException("val2 must be IConvertible type");

        // convert val2 to type of val1.
        var converted2 = Convert.ChangeType(val2, val1.GetType());

        // compare now that same type.
        return val1.Equals(converted2);
    }

И это будет работать для объекта:

        object obj1 = 1;
        object obj2 = 1.0;

        Console.WriteLine(ValueEquality(obj1, obj2));

Как я уже сказал, оба они могут сосуществовать как перегрузки, поэтому, если вы сравниваете совместимые типы IConvertible напрямую, он будет использовать общий тип, и если у вас есть только бокс-типы в качестве объекта, он будет использовать перегрузку объекта.

Ответ 2

Посмотрите на использование IComparable вместо ручного, если - http://msdn.microsoft.com/en-us/library/system.icomparable.compareto.aspx.

Если вам нужно что-то подобное в будущем, рассмотрите swith по типам первого операнда и реализуйте класс "обработчик операций" для каждого из типов с методом обработки операции, например IntOpHandler.PerformOp(int left, object right).

Вы также можете часто уменьшать количество типов, с которыми вам нужно иметь дело, объединив сначала несколько типов (например, байты, короткие, ushort, int, uint, long-cast до long, а затем выполнить длинные операции).