! = - оператор не работает для структуры - PullRequest
0 голосов
/ 26 апреля 2018

У меня есть несколько классов, которые имеют некоторые атрибуты сериализации для XmlSerializer, например XmlElement или XmlArray. Теперь я хочу использовать эти существующие атрибуты и для Json-сериализации.

Для этого я создал класс, производный от NewtonSoft´s DefaultContractResolver:

public class XmlToJsonContractResolver : DefaultContractResolver
{
    internal void ConfigureProperty(MemberInfo member, JsonProperty property)
    {
        ConfigureNamedMembers(member, property);
    }

    private static void ConfigureNamedMembers(MemberInfo member, JsonProperty property)
    {
        foreach (var attribute in member.GetCustomAttributesData().Where(x => x.AttributeType.Namespace == typeof(XmlElementAttribute).Namespace))
        { 
            // the elementName may either be stored in the named argument "ElementName" or was provided to the 
            // constructor of the appropriate Xml-attribute
            var item = attribute.NamedArguments?.FirstOrDefault(x => x.MemberName == "ElementName");
            if (item.HasValue && item.Value != default(CustomAttributeNamedArgument))
            {
                property.PropertyName = item.Value.MemberName;
                return;
            }

            // ElementName is provided to constructor of xml-attribute
            throw new NotImplementedException();
        }
    }

Теперь у меня есть тестовый класс для тестирования ConfigureProperty -метода сверху:

private class A
{
    [XmlElement("AnIntAlias")]
    public int AnXmlSerializableInt { get; set; }
}

Как видите, XmlElementAttribute не имеет именованных аргументов. Вместо этого ElementName -proerty устанавливается через его конструктор, поэтому код выше сначала проверяет наличие именованных аргументов.

Это код, который использует A:

var target = new XmlToJsonContractResolver();
var jsonProperty = new JsonProperty { PropertyName = memberName };
var member = typeof(A).GetMember(nameof(A.AnXmlSerializableInt)).First();
target.ConfigureProperty(member, jsonProperty);

var result = jsonProperty.PropertyName;

Когда я отлаживаю этот код, я выполняю первое условие if (item.HasValue && item.Value != default(CustomAttributeNamedArgument)), поэтому я получаю NullReferenceException при property.PropertyName = item.Value.MemberName;.

Когда я добавляю часы на attribute.NamedArguments (который является List<CustomAttributeNamedArgument> и, следовательно, списком типов значений), в этом списке есть ноль элементов, поэтому FirstOrDefault должно возвращать любое значение по умолчанию для этого структура. Однако != -оператор возвращает true.

1 Ответ

0 голосов
/ 26 апреля 2018

Это потому, что CustomAttributeNamedArgument struct переопределяет свои операторы равенства следующим образом:

public static bool operator ==(CustomAttributeNamedArgument left, CustomAttributeNamedArgument right)
{
     return left.Equals((object) right);
}

public override bool Equals(object obj)
{
    return obj == (ValueType)this;
}

Таким образом, в основном используется семантика равенства ссылок, даже если это структура. Почему именно это происходит, мне непонятно, но в этом причина вашей проверки. Даже это:

default(CustomAttributeNamedArgument) == default(CustomAttributeNamedArgument);

Возвращает false с такой семантикой.

Чтобы исправить, вы можете использовать что-то вроде

var item = attribute.NamedArguments?.Where(x => x.MemberName == "ElementName")
    .Cast<CustomAttributeNamedArgument?>().FirstOrDefault();                
if (item.HasValue) {
    property.PropertyName = item.Value.MemberName;
    return;
}

или аналогичный способ вернуть ноль в качестве значения по умолчанию.

...