Каскадирование влияния атрибута на переопределенные свойства в дочерних классах

Можно ли пометить свойство в базовом классе некоторым атрибутом, который также остается эффективным в дочерних классах?

Вопрос может быть очень специфичным для Serialization, но я определенно думаю, что могут быть и другие применения.

Рассмотрим следующий код:

using System;
using System.IO;
using System.Xml.Serialization;

namespace Code.Without.IDE
{
    [Serializable]
    public abstract class C1
    {
        [XmlIgnore]
        public abstract bool IsValid_C1 { get; set;}
    }

    [Serializable]
    public class C2 : C1
    {
        public bool IsValid_C2 { get; set; }

        public override bool IsValid_C1 { get; set;}

        public C2()
        {
            IsValid_C1 = true;
            IsValid_C2 = false;
        }
    }

    public static class AbstractPropertiesAttributeTest
    {
        public static void Main(string[] args)
        {
            C2 c2 = new C2();
            using(MemoryStream ms = new MemoryStream())
            {
                XmlSerializer ser = new XmlSerializer(typeof(C2));
                ser.Serialize(ms, c2);
                string result = System.Text.Encoding.UTF8.GetString(ms.ToArray());
                Console.WriteLine(result);
            }
        }
    }
}

Выше кода возвращается:

------ C:\abhi\Code\CSharp\without IDE\AbstractPropertiesAttributeTest.exe 
<?xml version="1.0"?>
<C2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <IsValid_C2>false</IsValid_C2>
  <IsValid_C1>true</IsValid_C1>
</C2>
------ Process returned 0

Я думал, что IsValid_C1 будет проигнорирован, хотя это не так. Есть ли способ достичь этого, кроме обозначения свойства как защищенного?

Изменить: быстрый код, показывающий, что атрибут XmlIgnore наследуется. http://ideone.com/HH41TE

Ответ 1

Я не верю, что существует способ наследования атрибута, поскольку вы переопределяете свойство базового класса. Вам нужно будет украсить IsValid_C1 из C2 с помощью XmlIgnore:

    [Serializable]
    public class C2 : C1
    {
        public bool IsValid_C2 { get; set; }

        [XmlIgnore]
        public override bool IsValid_C1 { get; set; }

        public C2()
        {
            IsValid_C1 = true;
            IsValid_C2 = false;
        }
    }

Ответ 2

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

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

Основываясь на этом мышлении, я предоставляю вам образец шаблона проектирования Decorator, смешанный с шаблоном проектирования стратегии. Если бы я разрабатывал классы, подобные тем, которые были на вашем образце, я бы это сделал:

    /// <summary>
    /// The interface for validation strategy (since we are using interface, there is no need for another abstract class)
    /// </summary>
    public interface IValidation
    {
        bool IsValid { get; set; }
    }

    /// <summary>
    /// The decorator (it dont need to be abstract) that has the serializable properties
    /// </summary>
    [Serializable]
    public class ValidatableDecorator : IValidation
    {
        protected IValidation instance;

        public ValidatableDecorator()
        {
            Init();
        }
        public ValidatableDecorator(IValidation instance)
        {
            Init();
        }

        protected virtual void Init() { }

        public void Set(IValidation instance)
        {
            this.instance = instance;
        }

        [XmlIgnore]
        public bool IsValid
        {
            get
            {
                return instance.IsValid;
            }
            set
            {
                instance.IsValid = value;
            }
        }
    }

Затем вам нужно реализовать некоторые классы, которые имеют логику шаблона стратегии, например:

    public class BossValidatorImplementation : IValidation
    {

        public bool IsValid
        {
            get
            {
                return false; ;
            }
            set
            {
                throw new InvalidOperationException("I dont allow you to tell me this!");
            }
        }
    }

    public class EasyGoingValidator : IValidation
    {
        public bool IsValid { get; set; }
    }

Теперь, когда у нас есть логика, отделенная от класса, мы можем наследовать от декораторов, выбирая стратегию, которую они используют для поля IsValid, например:

    public class ChildWithBossValidation : ValidatableDecorator
    {
        protected ChildWithBossValidation(IValidation instance)
            : this()
        {
            Init();
        }

        public ChildWithBossValidation()
            : base(new BossValidatorImplementation())
        {
            Init();
        }

        protected override void Init()
        {
            Name = "I'm the boss!";
            Sallary = 10000d;
        }

        public string Name { get; set; }
        public double Sallary { get; set; }

    }

    public class ChildWithEasyGoingValidation : ValidatableDecorator
    {
        public ChildWithEasyGoingValidation()
            : base(new EasyGoingValidator())
        {
        }
        protected ChildWithEasyGoingValidation(IValidation instance)
            : this()
        {
        }

        protected override void Init()
        {
            Name = "Do as you please... :)  ";
        }

        public string Name { get; set; }
    }

Это код, показывающий, что решение работает:

public static void Main(string[] args)
    {

        var boos = new ChildWithBossValidation();
        var coolGuy = new ChildWithEasyGoingValidation();

        using (var ms = new MemoryStream())
        {
            var ser = new XmlSerializer(boos.GetType());
            ser.Serialize(ms, boos);
            string result = System.Text.Encoding.UTF8.GetString(ms.ToArray());
            Console.WriteLine("With override");
            Console.WriteLine(result);
        }

        Console.WriteLine("-------------");

        using (var ms = new MemoryStream())
        {
            var ser = new XmlSerializer(coolGuy.GetType());
            ser.Serialize(ms, coolGuy);
            string result = System.Text.Encoding.UTF8.GetString(ms.ToArray());
            Console.WriteLine("With override");
            Console.WriteLine(result);
        }

        Console.ReadKey();
    }

Результат:

{<?xml version="1.0"?>
<ChildWithBossValidation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Name>I'm the boss!</Name>
  <Sallary>10000</Sallary>
</ChildWithBossValidation>-------------------<?xml version="1.0"?>
<ChildWithEasyGoingValidation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Name>Do as you please... :)  </Name>
</ChildWithEasyGoingValidation>}

Итак, возможно, это не отвечает на то, как каскадировать атрибут в этом случае (потому что вы можете легко сделать это, создав свой собственный атрибут (маркировка для разрешения наследования), а затем внедряя некоторый код в SerializeXML). Это еще один вариант, который может улучшить общую архитектуру решений с использованием шаблона проектирования. Но это также решает эту проблему:)

Ответ 3

Необходимое поведение может быть достигнуто с помощью классов XmlAttributeOverrides и XmlAttributes. Я написал вспомогательный метод для создания XmlSerializer:

    public static XmlSerializer GetXmlSerializerWithXmlIgnoreFields(Type t)
    {
        XmlAttributeOverrides xmlOverrides = new XmlAttributeOverrides();

        foreach (var prop in t.GetProperties(BindingFlags.Public|BindingFlags.Instance))
        {
            Attribute xmlIgnoreAttribute = Attribute.GetCustomAttribute(prop, typeof(XmlIgnoreAttribute));
            if (xmlIgnoreAttribute == null) 
                continue;

            XmlAttributes xmlAttributes = new XmlAttributes();
            xmlAttributes.XmlIgnore = true;
            xmlOverrides.Add(t, prop.Name, xmlAttributes);
        }

        return new XmlSerializer(t, xmlOverrides);
    }

Основной способ:

    public static void Main(string[] args)
    {
        C2 c2 = new C2();
        using (MemoryStream ms = new MemoryStream())
        {
            XmlSerializer ser = GetXmlSerializerWithXmlIgnoreFields(typeof(C2));
            ser.Serialize(ms, c2);
            string result = System.Text.Encoding.UTF8.GetString(ms.ToArray());
            Console.WriteLine(result);
        }
    }

Ответ 4

Похоже, эта функция нарушена на С#.

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