Сравнение типов значений в штучной упаковке - PullRequest
12 голосов
/ 12 июля 2011

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

((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)

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

Типы неизвестны до времени выполнения, где они могут быть любыми примитивными типами из источника данных. Это включает в себя строки, datetime, 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 что-нибудь, что я мог бы использовать для сравнения типов в штучной упаковке?

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

Ответы [ 2 ]

8 голосов
/ 12 июля 2011

Похоже, вы предполагаете, что тип из 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);
}

** ОБНОВЛЕНИЕ ** Сделаны оба типа обобщенных аргументов, могут быть выведены и добавляет больше безопасности времени компиляции на arg2, чтобы убедиться, что он IConvertible во время компиляции.

Учитывая эту универсальную функцию, все последующие теперь возвращают 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 напрямую, он будет использовать универсальный тип, а если вы просто используете коробочные типы в качестве объекта, он будет использовать перегрузку объекта.

3 голосов
/ 12 июля 2011

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

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

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

...