Математические операции над произвольными объектами в C # - PullRequest
4 голосов
/ 28 июля 2010

Я реализую интерпретатор для игрушечного языка в C #, и для математики на этом языке я хотел бы реализовать такую ​​функцию:

public static object Add( object a, object b )
{
  // return the sum of a and b
  // each might be int, double, or one of many other numeric types
}

Я могу представить себе оченьглупая и плохая реализация этой функции с множеством ветвей, основанных на типах a и b (с использованием оператора is) и кучей приведений, но мой инстинкт заключается в том, что есть лучший способ.

Что делатьВы думаете, что хорошая реализация этой функции будет?

Ответы [ 5 ]

19 голосов
/ 28 июля 2010

Если:

  • вам просто нужно простое в программировании решение
  • ваш маленький язык имеет те же арифметические правила, что и C #
  • вы можете использовать C # 4
  • вас не особо заботит производительность

тогда вы можете просто сделать это:

public static object Add(dynamic left, dynamic right)
{
    return left + right;
}

Готово. Что произойдет, когда этот метод будет вызван, код снова запустит компилятор C # и спросит компилятор "что бы вы сделали, если бы вам пришлось добавить эти две вещи, но вы знали их типы времени выполнения при компиляции" время?" (Динамическая языковая среда выполнения затем кеширует результат, так что в следующий раз, когда кто-то попытается добавить два целых числа, компилятор не запустится снова, он просто повторно использует лямбду, которую компилятор передал обратно в DLR.)

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

Способ, которым мы справляемся с этой сложностью в C #, состоит в том, что мы определяем операторы сложения для меньшего подмножества типов: int, uint, long, ulong, decimal, double, float, все перечисления, все делегаты, строки и все версии, допускающие значения NULL. из этих типов значений. (Перечисления затем обрабатываются как их базовые типы, что еще больше упрощает.)

Так, например, когда вы добавляете ushort к короткому, мы упрощаем проблему, говоря, что ushort и short являются частными случаями типа int, а затем решаем проблему добавления двух целых чисел. Это значительно сокращает объем кода, который мы должны написать. Но поверьте мне, алгоритм разрешения перегрузки бинарного оператора в C # - это тысячи строк кода. Это не простая проблема.

Если ваш игрушечный язык предназначен для использования в качестве динамического языка со своими собственными правилами, вы можете рассмотреть возможность реализации IDynamicMetaObjectProvider и использования механизмов DLR для реализации арифметических и других операций, таких как вызов функции.

6 голосов
/ 28 июля 2010

Преобразуйте ваши значения в самый широкий тип, например, в десятичный.Все типы, такие как int, double, short и т. Д., Реализуют интерфейс IConvertible (http://msdn.microsoft.com/en-us/library/system.iconvertible_members.aspx).. Он предоставляет метод ToDecimal, который можно использовать для преобразования значения в десятичный тип. Также очень полезен класс Convert

decimal aop = Convert.ToDecimal(a);
decimal bop = Convert.ToDecimal(b);
decimal sum = aop + bop;
return Convert.ChangeType(sum, typeof(a)); // Changing type from decimal to type of the first operand.
1 голос
/ 28 июля 2010

Самый простой способ сделать это прямо сейчас - просто протестировать типы, как вы сказали.

Но так как это для реализации игрушечного языка (хорошо для вас!), Тогда я бы предложил использовать лучшую абстракцию, чем object, для передачи значений в вашем интерпретаторе. возможно, создайте базовый класс LanguageNameObject, а затем вы можете добавить все вспомогательные методы, которые вы хотите помочь вам реализовать этот метод. По сути, класс Object - плохая абстракция для работы с вами ... так что создайте лучший класс!

1 голос
/ 28 июля 2010

Одна вещь, которую вы можете сделать, это написать собственную базу объектов для языка игрушек.Это может быть либо реальный класс, либо интерфейс.Таким образом, вы можете убедиться, что все ваши классы имеют какую-то функциональность для всех операций (даже если это только для того, чтобы вызвать исключение NotSupported во время выполнения).Вы можете сделать это, используя интерфейс или абстрактные функции для действительно распространенных вещей (например, ToString или Equals), или используя передачу сообщений или какой-либо другой метод для необычных операций.Мне нравится идея STO для числовых типов.)

0 голосов
/ 28 июля 2010

Хм, интересно, я бы использовал оператор typeof () для входящих объектов и проверил их по типам, которые позволят выполнять операции добавления. Я также предположил бы, что вы выбросите исключение, если типы странных шаров будут добавлены вместе?

В идеале, однако, если вы собираетесь разрешить добавление int, float, double и т. Д., Я бы просто создал перегруженную версию метода Add для обработки этих различных случаев.

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