Модераторы: Partizan, gambit

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Как сложить 2 объекта? (красиво и коротко) 
V
    Опции темы
Лен
Дата 12.7.2010, 12:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 73
Регистрация: 20.1.2009

Репутация: нет
Всего: нет



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

public static object Add(object arg1, object arg2)
{
  return arg1 + arg2;
}

Проблема в том, что в качестве и arg1, и arg2 могут выступать:
  • byte, byte?, sbyte, sbyte?
  • short, short?, ushort, ushort?
  • int, int?, uint, uint?
  • long, long?, ulong, ulong?
  • float, float?, double, double?, decimal, decimal?
  • char, char?, string
  • DateTime, DateTime?, TimeSpan, TimeSpan?
Типы arg1, arg2 и результата могут не совпадать: например, к строке можно добавить число (получая конкатенацию), к DateTime - TimeSpan, в результате сложения short и ushort можно получить int и т.д. Generic-и использовать не получится, т.к. конкретные значения и их типы не известны на этапе компиляции. Если сложение невозможно (например, пользователь решил прибавить к дате число, или к числу строку), может (и должно) появиться исключение (генерируемое самим .NET).

Как просто выполнить такое сложение, не создавая тьму методов с жёсткой типизацией (с выбором нужного метода в if/switch на 100 строк длиной)?
PM MAIL   Вверх
Artemon
Дата 12.7.2010, 12:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


а ты мне нравишься
***


Профиль
Группа: Завсегдатай
Сообщений: 1771
Регистрация: 24.2.2004
Где: Челябинск

Репутация: 1
Всего: 20



Если задачу упростить до того, чтобы возвращаемый тип значение всегда совпадал с типами аргументов, то можно решить с помощью одного шаблонного метода.




--------------------
Контроль топлива на топливозаправщиках, мониторинг автотранспорта, расчет зарплаты водителей www.rscat.ru
PM MAIL   Вверх
Mephisto
Дата 12.7.2010, 12:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Волкъ
***


Профиль
Группа: Завсегдатай
Сообщений: 1818
Регистрация: 27.8.2003
Где: Питер

Репутация: 2
Всего: 34



Цитата(Artemon @  12.7.2010,  13:34 Найти цитируемый пост)
Если задачу упростить до того, чтобы возвращаемый тип значение всегда совпадал с типами аргументов, то можно решить с помощью одного шаблонного метода.

для шаблонов запрещены операции +, -, * и прочие.
Цитата(Лен @  12.7.2010,  13:00 Найти цитируемый пост)
Generic-и использовать не получится, т.к. конкретные значения и их типы не известны на этапе компиляции. 

тут правильно написано.
PM   Вверх
mrbrooks
Дата 12.7.2010, 13:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


трололомен
****


Профиль
Группа: Завсегдатай
Сообщений: 4259
Регистрация: 4.10.2006
Где: Дол Гулдур

Репутация: 7
Всего: 306



Не будем категоричны.

Код

        public static T Add<T>(T a, T b)
        {
            ParameterExpression paramA = Expression.Parameter(typeof(T), "a"),
                                paramB = Expression.Parameter(typeof(T), "b");
            BinaryExpression body = Expression.Add(paramA, paramB);
            Func<T, T, T> add = Expression.Lambda<Func<T, T, T>>(body, paramA, paramB).Compile();
            return add(a, b);
        }

PM MAIL   Вверх
Лен
Дата 12.7.2010, 14:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 73
Регистрация: 20.1.2009

Репутация: нет
Всего: нет



Извиняюсь за то, что сразу не уточнил: используется .Net версии 2.0 (т.е. лямбда-выражения и прочие радости жизни недоступны).

Оговорюсь насчёт generic-ов: функция должна быть вида не 
Код
T Add<T>(T a, T b)
, а 
Код
object Add<TA, TB>(TA a, TB b)


Теоретически можно написать всё вот так:
Код

private static object add_int_int(object a, object b)
{
  return (int)a+(int)b;
}

private static object add_int_uint(object a, object b)
{
  return (int)a+(uint)b;
}

private static object add_uint_int(object a, object b)
{
  return (uint)a+(int)b;
}

private static object add_uint_uint(object a, object b)
{
  return (uint)a+(uint)b;
}

// все, надеюсь, помнят, как работает упаковка/распаковка
...

private static object add_string_sbyte(object a, object b)
{
  return string.Format("{0}{1}", a, b);
}

...

private static object add_datetime_timespan(object a, object b)
{
  return (DateTime)a + (TimeSpan)b;
}

...

private static object add_int_string(object a, object b)
{
  throw new NotSupportedException("Сложение объектов данных типов не поддерживается!");
}

// список всех методов для быстрого вызова
private static readonly IDictionary<KeyValuePair<Type, Type>, Function<object, object, object>> methods = new Dictionary<KeyValuePair<Type, Type>, Function<object, object, object>>
{
  {new KeyValuePair<Type, Type>(typeof(int), typeof(int)), add_int_int},
  {new KeyValuePair<Type, Type>(typeof(int), typeof(uint)), add_int_uint},
  {new KeyValuePair<Type, Type>(typeof(uint), typeof(int)), add_uint_int},
  {new KeyValuePair<Type, Type>(typeof(uint), typeof(uint)), add_uint_uint},
  ...
  {new KeyValuePair<Type, Type>(typeof(string), typeof(sbyte)), add_string_sbyte},
  ...
  {new KeyValuePair<Type, Type>(typeof(DateTime), typeof(TimeSpan)), add_datetime_timespan},
  ...
  {new KeyValuePair<Type, Type>(typeof(int), typeof(string)), add_int_string}
};

// собственно, public-метод
public static object Add(object arg1, object arg2)
{
  return methods[new KeyValuePair(arg1.GetType(), arg2.GetType())](arg1, arg2);
}


Работать оно будет быстро, но я не имею желания писать копипастом 29^2=841 метод! Должен же быть более элегантный способ. Может, ilasm поможет?
PM MAIL   Вверх
diadiavova
Дата 12.7.2010, 14:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Доктор Зло(диагност, настоящий, с лицензией и полномочиями)
****


Профиль
Группа: Модератор
Сообщений: 5821
Регистрация: 14.8.2008
Где: В Коньфпольте

Репутация: 31
Всего: 142



На бейсике напиши
Код

    Public Shared Function Add(ByVal a, ByVal b)
        Return a + b
    End Function
 smile 


--------------------
Хочешь получить мудрый совет - читай подписи участников форумов.
Злой доктор Щасзаболит smile
PM   Вверх
mrbrooks
Дата 12.7.2010, 16:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


трололомен
****


Профиль
Группа: Завсегдатай
Сообщений: 4259
Регистрация: 4.10.2006
Где: Дол Гулдур

Репутация: 7
Всего: 306



хы.

Цитата(Лен @  12.7.2010,  15:30 Найти цитируемый пост)
Теоретически можно написать всё вот так:

корректней было бы просто использовать перегрузку метода add, а не писать 841 метод add_X_X. Хотя конечно перегрузок тоже будет 841  smile 

в такой ситуации спасет наверно только отражение.
PM MAIL   Вверх
Лен
Дата 12.7.2010, 16:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 73
Регистрация: 20.1.2009

Репутация: нет
Всего: нет



Цитата
в такой ситуации спасет наверно только отражение.

Подскажите, как... Ибо  даже не догадываюсь...
PM MAIL   Вверх
diadiavova
Дата 12.7.2010, 17:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Доктор Зло(диагност, настоящий, с лицензией и полномочиями)
****


Профиль
Группа: Модератор
Сообщений: 5821
Регистрация: 14.8.2008
Где: В Коньфпольте

Репутация: 31
Всего: 142



Цитата(mrbrooks @  12.7.2010,  17:35 Найти цитируемый пост)
в такой ситуации спасет наверно только отражение

А что, при использовании отражения приведение типов не нужно что ли?
Цитата(Лен @  12.7.2010,  17:44 Найти цитируемый пост)
Подскажите, как... Ибо  даже не догадываюсь...

Надо использовать либо метод Type.InvokeMember, либо MethodInfo.Invoke. Операторы компилятся в методы со специальными именами, список здесь
http://msdn.microsoft.com/ru-ru/library/ms229032.aspx
Проблема только в том, что логику приведения типов тебе в этом случае все равно придется реализовывать самому. А если так, то можно обойтись и без отражения.

А вот на счет бейсика это была не шутка. В бейсике такой код работает.

Это сообщение отредактировал(а) diadiavova - 12.7.2010, 17:23


--------------------
Хочешь получить мудрый совет - читай подписи участников форумов.
Злой доктор Щасзаболит smile
PM   Вверх
mrbrooks
Дата 12.7.2010, 17:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


трололомен
****


Профиль
Группа: Завсегдатай
Сообщений: 4259
Регистрация: 4.10.2006
Где: Дол Гулдур

Репутация: 7
Всего: 306



Цитата(diadiavova @  12.7.2010,  18:17 Найти цитируемый пост)
А что, при использовании отражения приведение типов не нужно что ли?

Я утверждал обратное?

Цитата(diadiavova @  12.7.2010,  18:17 Найти цитируемый пост)
Проблема только в том, что логику приведения типов тебе в этом случае все равно придется реализовывать самому.

Да.

Цитата(diadiavova @  12.7.2010,  18:17 Найти цитируемый пост)
А если так, то можно обойтись и без отражения.

Да.


PM MAIL   Вверх
diadiavova
Дата 12.7.2010, 17:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Доктор Зло(диагност, настоящий, с лицензией и полномочиями)
****


Профиль
Группа: Модератор
Сообщений: 5821
Регистрация: 14.8.2008
Где: В Коньфпольте

Репутация: 31
Всего: 142



Цитата(mrbrooks @  12.7.2010,  18:32 Найти цитируемый пост)
Я утверждал обратное?

Ты утверждал, что отражение спасет, а тут главная проблема в том, что для выполнения операции над разнотипными аргументами как правило требуется приведение типов. Так что отражение тут вряд ли может спасти(если, конечно, я правильно понял идею, что не факт). Тут нужна либо ручная реализация логики приведения типов(а это вряд ли получится коротко), либо спасет бейсик smile с отключенной опцией Strict. Правда в последнем случае придется положиться на логику компилятора бейсика.

Это сообщение отредактировал(а) diadiavova - 12.7.2010, 17:48


--------------------
Хочешь получить мудрый совет - читай подписи участников форумов.
Злой доктор Щасзаболит smile
PM   Вверх
Void
Дата 12.7.2010, 19:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


λcat.lolcat
****


Профиль
Группа: Участник Клуба
Сообщений: 2206
Регистрация: 16.11.2004
Где: Zürich

Репутация: 25
Всего: 173



Похоже на реализацию какого-то динамического языка на C#. Создатели таких языков как-то обходятся без N^2 методов для каждого бинарного оператора. Мне кажется, проще всего написать цепочку if, повторяющую логику компилятора. Я бы скукожил численные типы до long, double и decimal, но если уж хочется точно как в C#:
Код

private static HashSet<Type> numericTypes = new HashSet<Type>(new Type[] {
    typeof(sbyte), typeof(byte), typeof(short), typeof(ushort),
    typeof(int), typeof(uint), typeof(long), typeof(ulong),
    typeof(float), typeof(double), typeof(decimal)});

private static bool IsNumeric(object x)
{
    return numericTypes.Contains(x.GetType());
}

private static bool IsReal(object x)
{
    return x is float || x is double;
}

public static object Add(object a, object b)
{
    if (IsNumeric(a) && IsNumeric(b))
    {
        if (a is decimal || b is decimal)
        {
            if (!(IsReal(a) || IsReal(b)))
            {
                return (decimal)a + (decimal)b;
            }
            else
            {
                throw new ArgumentException();
            }
        }
        if (a is double || b is double)
        {
            return (double)a + (double)b;
        }
        if (a is float || b is float)
        {
            return (float)a + (float)b;
        }
        if (a is long && b is ulong || a is ulong && b is long)
        {
            throw new ArgumentException();
        }
        if (a is long || b is long)
        {
            return (long)a + (long)b;
        }
        if (a is ulong && b is ulong)
        {
            return (ulong)a + (ulong)b;
        }
        if (a is uint && b is uint)
        {
            return (uint)a + (uint)b;
        }
        return (int)a + (int)b;
    }
    if (a is string || b is string)
    {
        return a.ToString() + b.ToString();
    }
    if (a is DateTime && b is TimeSpan)
    {
        return (DateTime)a + (TimeSpan)b;
    }
    throw new ArgumentException();
}

Для остальных арифметических операций правила те же самые, но придётся копипастить. Обобщить, к сожалению, невозможно. Если бы в C# озаботились хоть какой-то явной реализацией numerical tower (interface INumeric<T> или что-то в этом духе), было бы гораздо проще.


--------------------
“Coming back to where you started is not the same as never leaving.” — Terry Pratchett
PM MAIL WWW GTalk   Вверх
mrbrooks
Дата 12.7.2010, 19:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


трололомен
****


Профиль
Группа: Завсегдатай
Сообщений: 4259
Регистрация: 4.10.2006
Где: Дол Гулдур

Репутация: 7
Всего: 306



Цитата(diadiavova @  12.7.2010,  18:47 Найти цитируемый пост)
Ты утверждал

Не - не - не. Я предполагал smile
Приведение хорошо уже тем - что меньше пИсать кода.

Твои аргументы убедительны - однако просто приведение приведет к 
Цитата(Лен @  12.7.2010,  13:00 Найти цитируемый пост)
Как просто выполнить такое сложение, не создавая тьму методов с жёсткой типизацией (с выбором нужного метода в if/switch на 100 строк длиной)?


Хотелось бы конечно красивого решения, но увы, похоже гламур возможен только в Net 3.5 а уж про dynamic из четвертого - это вообще отвал башки.

Добавлено через 1 минуту и 16 секунд
Void, ужОс. А что делать.  smile

Добавлено через 4 минуты и 53 секунды
Вообще была мысль создать Hashtable с ключем Type и с келлбеком функции. Но что из этого получится - я не знаю  smile  
PM MAIL   Вверх
diadiavova
Дата 12.7.2010, 20:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Доктор Зло(диагност, настоящий, с лицензией и полномочиями)
****


Профиль
Группа: Модератор
Сообщений: 5821
Регистрация: 14.8.2008
Где: В Коньфпольте

Репутация: 31
Всего: 142



Цитата(mrbrooks @  12.7.2010,  20:44 Найти цитируемый пост)
Твои аргументы убедительны - однако просто приведение приведет к 
Цитата(Лен @  12.7.2010,  13:00 )
Как просто выполнить такое сложение, не создавая тьму методов с жёсткой типизацией (с выбором нужного метода в if/switch на 100 строк длиной)?

Ну я это и имел в виду, когда писал
Цитата(diadiavova @  12.7.2010,  18:47 Найти цитируемый пост)
а это вряд ли получится коротко

Есть, конечно, лазейки(типа выявления числовых типов итд), но в любом случае это не просто и многа букафф smile . 

Кстати функция IsNumeric  в дотнете есть
http://msdn.microsoft.com/ru-ru/library/6c...1(v=VS.90).aspx



--------------------
Хочешь получить мудрый совет - читай подписи участников форумов.
Злой доктор Щасзаболит smile
PM   Вверх
Лен
Дата 13.7.2010, 14:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 73
Регистрация: 20.1.2009

Репутация: нет
Всего: нет



diadiavova, вариант с Бейсиком пока отложу на полку подальше (но буду иметь в виду), ибо добавлять отдельную dll на бейсике для одного-единственного метода - не комильфо. Попробовал рефлексией (MethodInfo.Invoke) - методы с укзанными в приведенной статье именами через Type.GetMethod(string name) не находятся.

Void, почти угадали - это виртуальная машина для примитивного скриптового C#-подобного языка. Только код в варианте с кучей if-ов (как у Вас) я отверг сразу - реализация 11 методов с 2мя аргументами, каждый из которых может быть одного из 29 типов подобным образом займёт чёртову уйму времени, да и куча копипастов ещё никого до добра не довела.

Пока что остановился на компромиссном варианте: с помощью нескольких if-ов отсеиваю тривиальные варианты (строки, даты и т.п.), а с числами буду работать по принципу: "реализовать метод для long/ulong/double" -> "привести оба аргумента к одному из этих типов" -> "вызвать метод". Когда всё получится - выложу код.
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Прежде чем создать тему, посмотрите сюда:
mr.DUDA
THandle

Используйте теги [code=csharp][/code] для подсветки кода. Используйтe чекбокс "транслит" если у Вас нет русских шрифтов.
Что делать если Вам помогли, но отблагодарить помощника плюсом в репутацию Вы не можете(не хватает сообщений)? Пишите сюда, или отправляйте репорт. Поставим :)
Так же не забывайте отмечать свой вопрос решенным, если он таковым является :)


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, mr.DUDA, THandle.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Общие вопросы по .NET и C# | Следующая тема »


 




[ Время генерации скрипта: 0.0585 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.