Это выглядит как ошибка при удалении к нулю операндов на общих структурах.
Рассмотрим следующую фиктивную структуру, которая переопределяет operator==
:
struct MyStruct
{
private readonly int _value;
public MyStruct(int val) { this._value = val; }
public override bool Equals(object obj) { return false; }
public override int GetHashCode() { return base.GetHashCode(); }
public static bool operator ==(MyStruct a, MyStruct b) { return false; }
public static bool operator !=(MyStruct a, MyStruct b) { return false; }
}
Теперь рассмотрим следующие выражения:
Expression<Func<MyStruct, MyStruct, bool>> exprA =
(valueA, valueB) => valueA == valueB;
Expression<Func<MyStruct?, MyStruct?, bool>> exprB =
(nullableValueA, nullableValueB) => nullableValueA == nullableValueB;
Expression<Func<MyStruct?, MyStruct, bool>> exprC =
(nullableValueA, valueB) => nullableValueA == valueB;
Все три компилируются и запускаются, как ожидалось.
Когда они скомпилированы (используя .Compile()
), они создают следующий код (перефразируемый на английском языке от IL):
-
Первое выражение, которое принимает только
MyStruct
(не нулевое) args, просто вызываетop_Equality
(наша реализацияoperator ==
) -
Второе выражение при компиляции создает код, который проверяет каждый аргумент, чтобы увидеть, если он
HasValue
. Если оба параметра (оба равныnull
), возвращаетtrue
. Если только один имеет значение, возвращаетfalse
. В противном случае вызовыop_Equality
для двух значений. -
Третье выражение проверяет аргумент nullable, чтобы увидеть, имеет ли он значение - если нет, возвращает false. В противном случае вызовы
op_Equality
.
Пока все хорошо.
Следующий шаг: выполните то же самое с общим типом - измените MyStruct
на MyStruct<T>
всюду в определении типа и измените его на MyStruct<int>
в выражениях.
Теперь третье выражение компилируется, но генерирует исключение времени выполнения InvalidOperationException
со следующим сообщением:
Операнды для оператора "Равно" не соответствуют параметрам метода "op_Equality".
Я ожидал бы, что общие структуры будут вести себя точно так же, как и не общие, со всеми описанными выше обнулениями.
Итак, мои вопросы:
- Почему существует различие между родовыми и не-генерическими структурами?
- В чем смысл этого исключения?
- Является ли это ошибкой в С#/. NET?
Полный код для воспроизведения этого доступен в этом стиле.