2010-07-28 6 views
4

Я реализует интерпретатор для игрушечного языка в C#, и для того, чтобы сделать математику на этом языке я хотел бы реализовать такую ​​функцию:Математические операции на произвольных объектов в 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) и кучей отливок, но мой инстинкт в том, что есть лучший способ.

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

ответ

19

Если:

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

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

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

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

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

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

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

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

+0

Эрик, я ценю этот продуманный и информативный ответ. Я определенно не ожидал ничего такого качества, когда задавал вопрос! Доброе спасибо. – Harold

+0

См. Мой комментарий вместо этого относительно перегрузки оператора. На мой взгляд, это лучше, и для этого были сделаны перегрузки оператора. –

0

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

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

+0

Согласитесь с перегрузкой – Pieces

6

Преобразуйте свои значения в самый широкий тип, например, в десятичный. Все типы, такие как 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. 
+0

Это интересное расширение/вариация идеи «просто сделайте все десятичной». Спасибо, что поделились этим. – Harold

1

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

(постскриптум я пересекаю отправленный с STO, но мне нравится идея STO для числовых типов.)

1

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

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

Смежные вопросы