Как примечание стороны, проблема со Страшным Ромбом состоит в том, что базовый класс присутствует многократно. Таким образом с регулярным наследованием, Вы полагаете, что имеете:
A
/ \
B C
\ /
D
, Но в расположении памяти, Вы имеете:
A A
| |
B C
\ /
D
Это объясняет почему, когда вызов D::foo()
, у Вас есть проблема неоднозначности. Но реальный проблема возникает, когда Вы хотите использовать членскую переменную A
. Например, скажем, мы имеем:
class A
{
public :
foo() ;
int m_iValue ;
} ;
, Когда Вы попытаетесь получить доступ m_iValue
от D
, компилятор выступит, потому что в иерархии, он будет видеть два m_iValue
, не один. И если Вы измените тот, скажем, B::m_iValue
(который является A::m_iValue
родитель [1 110]), [то 1111] не будет изменен (который является A::m_iValue
родитель [1 113]).
Это - то, куда виртуальное наследование происходит удобное, как с ним, Вы возвратитесь к истинному ромбовидному расположению, с не только один foo()
метод только, но также и один и только один m_iValue
.
Вообразите:
A
имеет некоторую основную характеристику. B
добавляет к нему, некоторый прохладный массив данных (например) C
добавляет к нему некоторую замечательную опцию как шаблон "наблюдатель" (например, на [1 119]). D
наследовался от [1 121] и C
, и таким образом от [1 123]. С нормальным наследованием, изменяя m_iValue
от [1 125] неоднозначно, и это должно быть разрешено. Даже если это, существует два m_iValues
внутреннее D
, таким образом, необходимо помнить, что и обновляют два одновременно.
С виртуальным наследованием, изменяя m_iValue
от [1 129] в порядке... Но... Скажем, то, что Вы имеете D
. Через C
интерфейс, Вы присоединили наблюдателя. И через B
интерфейс, Вы обновляете прохладный массив, который имеет побочный эффект прямого изменения m_iValue
...
, Поскольку изменение [1 134] сделано непосредственно (не используя виртуальный метод доступа), наблюдателя, "слушающего" до [1 135], не позвонят, потому что код, реализовывая слушание находится в [1 136], и B
не знает об этом...
, Если у Вас есть ромб в Вашей иерархии, это означает, что у Вас есть 95%, чтобы сделать что-то не так с упомянутой иерархией.
Да, пользовательские операторы вызываются с помощью отражения (у них есть специальные имена, такие как op_Addition), но System.Int32 не определяет их, поскольку основные, встроенные типы обрабатываются непосредственно кодами операций IL, например add
, а не вызовы методов.
Что именно вы хотите сделать? Работа с различными значениями операторов (примитивными (сопоставленными с конкретными инструкциями IL), настраиваемыми (сопоставленными со статическими методами) и поднятыми (предоставляемыми компилятором в качестве шаблона)) делает это болезненным. Если вы просто хотите использовать операторы, то можно написать код, обеспечивающий поддержку операторов через дженерики . У меня есть код для этого, который находится в свободном доступе в MiscUtil ( описание и примеры ).
В качестве нетипизированного примера (обратите внимание, что это не очень эффективно, но работает ):
object x = 123, y = 345; // now forget that we know that these are ints...
object result = Expression.Lambda<Func<object>>(
Expression.Convert(Expression.Add(
Expression.Constant(x), Expression.Constant(y)),
typeof(object))).Compile()();
Было бы очень неэффективно, если бы для добавления или сравнения двух целых чисел требовался вызов метода, поэтому эти простые операции с основными типами генерируются кодом, как описано в другом ответе, и не могут быть вызваны с помощью отражения. Один интересный встроенный тип значения - это decimal
(или System.Decimal
). Эта структура
поддерживает буквальные значения в C #, но ведет себя во многом как реальный тип значения и предоставляет множество операторов, которые можно вызывать через отражение.
любопытно, что вы можете использовать Reflector для просмотра всех элементов, представленных System.Int32
.
Здесь есть хорошие ответы, так что просто чтобы добавить еще не упомянутое:
В структуре может быть перегружен оператор . Это означает больше вариантов для проверки, пытаетесь ли вы создать какой-либо программный подход.
Хорошая вещь, которую стоит попробовать, - это попробовать подход дерева выражений. Вот небольшой образец. Конечно, производительность не слишком хороша, но мы все равно знаем, что происходит с Reflection.